@geminixiang/mama 0.2.0-beta.1 → 0.2.0-beta.11
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/README.md +168 -371
- package/dist/adapter.d.ts +36 -12
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js.map +1 -1
- package/dist/adapters/discord/bot.d.ts +12 -7
- package/dist/adapters/discord/bot.d.ts.map +1 -1
- package/dist/adapters/discord/bot.js +358 -135
- package/dist/adapters/discord/bot.js.map +1 -1
- package/dist/adapters/discord/context.d.ts +1 -1
- package/dist/adapters/discord/context.d.ts.map +1 -1
- package/dist/adapters/discord/context.js +100 -36
- package/dist/adapters/discord/context.js.map +1 -1
- package/dist/adapters/shared.d.ts +71 -0
- package/dist/adapters/shared.d.ts.map +1 -0
- package/dist/adapters/shared.js +168 -0
- package/dist/adapters/shared.js.map +1 -0
- package/dist/adapters/slack/bot.d.ts +30 -24
- package/dist/adapters/slack/bot.d.ts.map +1 -1
- package/dist/adapters/slack/bot.js +613 -224
- package/dist/adapters/slack/bot.js.map +1 -1
- package/dist/adapters/slack/branch-manager.d.ts +22 -0
- package/dist/adapters/slack/branch-manager.d.ts.map +1 -0
- package/dist/adapters/slack/branch-manager.js +97 -0
- package/dist/adapters/slack/branch-manager.js.map +1 -0
- package/dist/adapters/slack/context.d.ts +1 -1
- package/dist/adapters/slack/context.d.ts.map +1 -1
- package/dist/adapters/slack/context.js +127 -72
- package/dist/adapters/slack/context.js.map +1 -1
- package/dist/adapters/slack/session.d.ts +3 -0
- package/dist/adapters/slack/session.d.ts.map +1 -0
- package/dist/adapters/slack/session.js +16 -0
- package/dist/adapters/slack/session.js.map +1 -0
- package/dist/adapters/slack/tools/attach.d.ts +1 -1
- package/dist/adapters/slack/tools/attach.d.ts.map +1 -1
- package/dist/adapters/slack/tools/attach.js.map +1 -1
- package/dist/adapters/telegram/bot.d.ts +4 -2
- package/dist/adapters/telegram/bot.d.ts.map +1 -1
- package/dist/adapters/telegram/bot.js +193 -147
- package/dist/adapters/telegram/bot.js.map +1 -1
- package/dist/adapters/telegram/context.d.ts.map +1 -1
- package/dist/adapters/telegram/context.js +58 -111
- package/dist/adapters/telegram/context.js.map +1 -1
- package/dist/adapters/telegram/html.d.ts +3 -0
- package/dist/adapters/telegram/html.d.ts.map +1 -0
- package/dist/adapters/telegram/html.js +98 -0
- package/dist/adapters/telegram/html.js.map +1 -0
- package/dist/agent.d.ts +9 -13
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +601 -567
- package/dist/agent.js.map +1 -1
- package/dist/commands/auto-reply.d.ts +16 -0
- package/dist/commands/auto-reply.d.ts.map +1 -0
- package/dist/commands/auto-reply.js +69 -0
- package/dist/commands/auto-reply.js.map +1 -0
- package/dist/commands/index.d.ts +5 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +19 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/login.d.ts +5 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +76 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/model.d.ts +14 -0
- package/dist/commands/model.d.ts.map +1 -0
- package/dist/commands/model.js +112 -0
- package/dist/commands/model.js.map +1 -0
- package/dist/commands/new.d.ts +9 -0
- package/dist/commands/new.d.ts.map +1 -0
- package/dist/commands/new.js +28 -0
- package/dist/commands/new.js.map +1 -0
- package/dist/commands/registry.d.ts +7 -0
- package/dist/commands/registry.d.ts.map +1 -0
- package/dist/commands/registry.js +14 -0
- package/dist/commands/registry.js.map +1 -0
- package/dist/commands/sandbox.d.ts +10 -0
- package/dist/commands/sandbox.d.ts.map +1 -0
- package/dist/commands/sandbox.js +88 -0
- package/dist/commands/sandbox.js.map +1 -0
- package/dist/commands/session-view.d.ts +5 -0
- package/dist/commands/session-view.d.ts.map +1 -0
- package/dist/commands/session-view.js +62 -0
- package/dist/commands/session-view.js.map +1 -0
- package/dist/commands/types.d.ts +41 -0
- package/dist/commands/types.d.ts.map +1 -0
- package/dist/commands/types.js +2 -0
- package/dist/commands/types.js.map +1 -0
- package/dist/commands/utils.d.ts +8 -0
- package/dist/commands/utils.d.ts.map +1 -0
- package/dist/commands/utils.js +14 -0
- package/dist/commands/utils.js.map +1 -0
- package/dist/config.d.ts +49 -30
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +313 -75
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts +10 -42
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +14 -127
- package/dist/context.js.map +1 -1
- package/dist/events.d.ts +13 -6
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +118 -64
- package/dist/events.js.map +1 -1
- package/dist/execution-resolver.d.ts +9 -5
- package/dist/execution-resolver.d.ts.map +1 -1
- package/dist/execution-resolver.js +82 -18
- package/dist/execution-resolver.js.map +1 -1
- package/dist/file-guards.d.ts +6 -0
- package/dist/file-guards.d.ts.map +1 -0
- package/dist/file-guards.js +48 -0
- package/dist/file-guards.js.map +1 -0
- package/dist/fs-atomic.d.ts +10 -0
- package/dist/fs-atomic.d.ts.map +1 -0
- package/dist/fs-atomic.js +45 -0
- package/dist/fs-atomic.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/instrument.d.ts.map +1 -1
- package/dist/instrument.js +4 -11
- package/dist/instrument.js.map +1 -1
- package/dist/log.d.ts +1 -5
- package/dist/log.d.ts.map +1 -1
- package/dist/log.js +13 -38
- package/dist/log.js.map +1 -1
- package/dist/{login.d.ts → login/index.d.ts} +16 -4
- package/dist/login/index.d.ts.map +1 -0
- package/dist/{login.js → login/index.js} +55 -17
- package/dist/login/index.js.map +1 -0
- package/dist/{link-server.d.ts → login/portal.d.ts} +7 -4
- package/dist/login/portal.d.ts.map +1 -0
- package/dist/login/portal.js +1453 -0
- package/dist/login/portal.js.map +1 -0
- package/dist/{link-token.d.ts → login/session.d.ts} +4 -3
- package/dist/login/session.d.ts.map +1 -0
- package/dist/{link-token.js → login/session.js} +1 -1
- package/dist/login/session.js.map +1 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +151 -373
- package/dist/main.js.map +1 -1
- package/dist/provisioner.d.ts +42 -52
- package/dist/provisioner.d.ts.map +1 -1
- package/dist/provisioner.js +256 -111
- package/dist/provisioner.js.map +1 -1
- package/dist/runtime/conversation-orchestrator.d.ts +42 -0
- package/dist/runtime/conversation-orchestrator.d.ts.map +1 -0
- package/dist/runtime/conversation-orchestrator.js +150 -0
- package/dist/runtime/conversation-orchestrator.js.map +1 -0
- package/dist/runtime/index.d.ts +2 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +2 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/session-runtime.d.ts +27 -0
- package/dist/runtime/session-runtime.d.ts.map +1 -0
- package/dist/runtime/session-runtime.js +211 -0
- package/dist/runtime/session-runtime.js.map +1 -0
- package/dist/sandbox/cloudflare.d.ts +15 -0
- package/dist/sandbox/cloudflare.d.ts.map +1 -0
- package/dist/sandbox/cloudflare.js +137 -0
- package/dist/sandbox/cloudflare.js.map +1 -0
- package/dist/sandbox/container.d.ts +2 -1
- package/dist/sandbox/container.d.ts.map +1 -1
- package/dist/sandbox/container.js +5 -1
- package/dist/sandbox/container.js.map +1 -1
- package/dist/sandbox/firecracker.d.ts +2 -1
- package/dist/sandbox/firecracker.d.ts.map +1 -1
- package/dist/sandbox/firecracker.js +6 -0
- package/dist/sandbox/firecracker.js.map +1 -1
- package/dist/sandbox/host.d.ts +2 -3
- package/dist/sandbox/host.d.ts.map +1 -1
- package/dist/sandbox/host.js +5 -5
- package/dist/sandbox/host.js.map +1 -1
- package/dist/sandbox/index.d.ts +6 -4
- package/dist/sandbox/index.d.ts.map +1 -1
- package/dist/sandbox/index.js +9 -6
- package/dist/sandbox/index.js.map +1 -1
- package/dist/sandbox/path-context.d.ts +4 -0
- package/dist/sandbox/path-context.d.ts.map +1 -0
- package/dist/sandbox/path-context.js +20 -0
- package/dist/sandbox/path-context.js.map +1 -0
- package/dist/sandbox/types.d.ts +17 -1
- package/dist/sandbox/types.d.ts.map +1 -1
- package/dist/sandbox/types.js.map +1 -1
- package/dist/sentry.d.ts +1 -1
- package/dist/sentry.d.ts.map +1 -1
- package/dist/sentry.js +4 -2
- package/dist/sentry.js.map +1 -1
- package/dist/session-policy.d.ts +13 -0
- package/dist/session-policy.d.ts.map +1 -0
- package/dist/session-policy.js +23 -0
- package/dist/session-policy.js.map +1 -0
- package/dist/session-store.d.ts +34 -3
- package/dist/session-store.d.ts.map +1 -1
- package/dist/session-store.js +184 -22
- package/dist/session-store.js.map +1 -1
- package/dist/session-view/command.d.ts +5 -0
- package/dist/session-view/command.d.ts.map +1 -0
- package/dist/session-view/command.js +11 -0
- package/dist/session-view/command.js.map +1 -0
- package/dist/session-view/portal.d.ts +16 -0
- package/dist/session-view/portal.d.ts.map +1 -0
- package/dist/session-view/portal.js +1742 -0
- package/dist/session-view/portal.js.map +1 -0
- package/dist/session-view/service.d.ts +34 -0
- package/dist/session-view/service.d.ts.map +1 -0
- package/dist/session-view/service.js +427 -0
- package/dist/session-view/service.js.map +1 -0
- package/dist/session-view/store.d.ts +18 -0
- package/dist/session-view/store.d.ts.map +1 -0
- package/dist/session-view/store.js +39 -0
- package/dist/session-view/store.js.map +1 -0
- package/dist/store.d.ts +3 -6
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +22 -48
- package/dist/store.js.map +1 -1
- package/dist/tool-diagnostics.d.ts +2 -0
- package/dist/tool-diagnostics.d.ts.map +1 -0
- package/dist/tool-diagnostics.js +7 -0
- package/dist/tool-diagnostics.js.map +1 -0
- package/dist/tools/bash.d.ts +1 -1
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/edit.d.ts +1 -1
- package/dist/tools/edit.d.ts.map +1 -1
- package/dist/tools/edit.js.map +1 -1
- package/dist/tools/event.d.ts +43 -2
- package/dist/tools/event.d.ts.map +1 -1
- package/dist/tools/event.js +48 -13
- package/dist/tools/event.js.map +1 -1
- package/dist/tools/index.d.ts +2 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +3 -3
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/read.d.ts +1 -1
- package/dist/tools/read.d.ts.map +1 -1
- package/dist/tools/read.js.map +1 -1
- package/dist/tools/write.d.ts +1 -1
- package/dist/tools/write.d.ts.map +1 -1
- package/dist/tools/write.js.map +1 -1
- package/dist/trigger.d.ts +31 -0
- package/dist/trigger.d.ts.map +1 -0
- package/dist/trigger.js +98 -0
- package/dist/trigger.js.map +1 -0
- package/dist/ui-copy.d.ts +1 -0
- package/dist/ui-copy.d.ts.map +1 -1
- package/dist/ui-copy.js +3 -0
- package/dist/ui-copy.js.map +1 -1
- package/dist/vault-routing.d.ts +1 -7
- package/dist/vault-routing.d.ts.map +1 -1
- package/dist/vault-routing.js +6 -48
- package/dist/vault-routing.js.map +1 -1
- package/dist/vault.d.ts +21 -55
- package/dist/vault.d.ts.map +1 -1
- package/dist/vault.js +144 -263
- package/dist/vault.js.map +1 -1
- package/package.json +12 -10
- package/dist/bindings.d.ts +0 -63
- package/dist/bindings.d.ts.map +0 -1
- package/dist/bindings.js +0 -94
- package/dist/bindings.js.map +0 -1
- package/dist/link-server.d.ts.map +0 -1
- package/dist/link-server.js +0 -839
- package/dist/link-server.js.map +0 -1
- package/dist/link-token.d.ts.map +0 -1
- package/dist/link-token.js.map +0 -1
- package/dist/login.d.ts.map +0 -1
- package/dist/login.js.map +0 -1
- package/dist/vault.test.d.ts +0 -2
- package/dist/vault.test.d.ts.map +0 -1
- package/dist/vault.test.js +0 -67
- package/dist/vault.test.js.map +0 -1
package/dist/provisioner.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provisioner.js","sourceRoot":"","sources":["../src/provisioner.ts"],"names":[],"mappings":"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","sourcesContent":["import { execFile } from \"child_process\";\nimport { promisify } from \"util\";\nimport * as log from \"./log.js\";\n\nconst execFileAsync = promisify(execFile);\ntype ExecFileAsync = typeof execFileAsync;\n\ntype ContainerStatus = \"running\" | \"stopped\" | \"missing\";\n\ninterface ContainerState {\n status: ContainerStatus;\n lastUsed: number;\n containerName: string;\n}\n\nexport interface ContainerMount {\n source: string;\n target: string;\n}\n\nexport interface ProvisionOptions {\n containerName?: string;\n mounts?: ContainerMount[];\n}\n\n// ── DockerContainerManager ─────────────────────────────────────────────────────\n\n/**\n * Manages the lifecycle of per-user Docker containers.\n *\n * Tracks each container's status in memory (running / stopped / missing).\n * State is always verified against Docker on provision(), so in-memory state\n * stays accurate without polling.\n */\nexport class DockerContainerManager {\n private state = new Map<string, ContainerState>();\n /**\n * In-flight provision() calls per vaultId. A concurrent second call for the\n * same user piggybacks on the first docker start/run instead of racing —\n * without this, two parallel messages from one user could produce duplicate\n * containers or conflict on docker run.\n */\n private inflight = new Map<string, Promise<string>>();\n private static readonly MANAGED_LABEL = \"mama.managed=true\";\n private static readonly IMAGE_MODE_LABEL = \"mama.sandbox=image\";\n private static readonly VAULT_ID_LABEL_KEY = \"mama.vault-id\";\n\n constructor(\n private readonly image: string,\n private readonly workspaceDir: string,\n private readonly execFileImpl: ExecFileAsync = execFileAsync,\n ) {}\n\n /** Sanitize an identifier segment for use in vault keys and container names. */\n static sanitizeSegment(value: string): string {\n const sanitized = value\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n return sanitized || \"unknown\";\n }\n\n /**\n * Deterministic vault key for a platform user.\n * e.g. (\"slack\", \"U04ABC\") → \"slack-u04abc\"\n */\n static vaultId(platform: string, platformUserId: string): string {\n return `${DockerContainerManager.sanitizeSegment(platform)}-${DockerContainerManager.sanitizeSegment(platformUserId)}`;\n }\n\n /** Deterministic container name for a vault-backed user sandbox. */\n static containerName(vaultId: string): string {\n return `mama-sandbox-${vaultId}`;\n }\n\n /**\n * Ensure a container exists and is running for the given vaultId.\n * Always inspects the actual Docker state, then acts accordingly:\n * - running → no-op\n * - stopped → docker start\n * - missing → docker run\n *\n * Returns the container name.\n */\n async provision(vaultId: string, options: ProvisionOptions = {}): Promise<string> {\n const existing = this.inflight.get(vaultId);\n if (existing) return existing;\n\n const pending = this.provisionInner(vaultId, options).finally(() => {\n this.inflight.delete(vaultId);\n });\n this.inflight.set(vaultId, pending);\n return pending;\n }\n\n private async provisionInner(vaultId: string, options: ProvisionOptions): Promise<string> {\n const containerName = options.containerName ?? DockerContainerManager.containerName(vaultId);\n const mounts = options.mounts ?? [];\n const status = await this.inspectStatus(containerName);\n\n try {\n if (status !== \"missing\" && (await this.hasBindMountDrift(containerName, mounts))) {\n log.logInfo(`Container ${containerName} mounts changed; recreating container`);\n await this.execFileImpl(\"docker\", [\"rm\", \"-f\", containerName]);\n await this.runContainer(vaultId, containerName, mounts);\n log.logInfo(`Container ${containerName} recreated`);\n } else if (status === \"running\") {\n log.logInfo(`Container ${containerName} already running`);\n } else if (status === \"stopped\") {\n await this.execFileImpl(\"docker\", [\"start\", containerName]);\n log.logInfo(`Container ${containerName} started`);\n } else {\n await this.runContainer(vaultId, containerName, mounts);\n log.logInfo(`Container ${containerName} created`);\n }\n } catch (err) {\n // Drop cached state so the next provision() re-inspects Docker cleanly\n // and stopIdle doesn't keep trying to stop a container that never\n // became running. We deliberately don't bump lastUsed here.\n this.state.delete(vaultId);\n throw err;\n }\n\n this.setState(vaultId, \"running\", containerName);\n return containerName;\n }\n\n /**\n * Stop a running container (docker stop). Container is preserved and can be\n * restarted via provision(). Intended for idle lifecycle management.\n */\n async stop(vaultId: string): Promise<void> {\n const containerName = this.getContainerName(vaultId);\n try {\n await this.execFileImpl(\"docker\", [\"stop\", containerName]);\n this.setState(vaultId, \"stopped\", containerName);\n log.logInfo(`Container ${containerName} stopped (idle)`);\n } catch (err) {\n log.logWarning(\n `Failed to stop container ${containerName}`,\n err instanceof Error ? err.message : String(err),\n );\n }\n }\n\n /** Stop and remove a container permanently (e.g. on vault revocation). */\n async remove(vaultId: string): Promise<void> {\n const containerName = this.getContainerName(vaultId);\n try {\n await this.execFileImpl(\"docker\", [\"rm\", \"-f\", containerName]);\n this.state.delete(vaultId);\n log.logInfo(`Container ${containerName} removed`);\n } catch (err) {\n log.logWarning(\n `Failed to remove container ${containerName}`,\n err instanceof Error ? err.message : String(err),\n );\n }\n }\n\n /**\n * Stop all containers that have been idle for longer than maxIdleMs.\n * Idle time is measured from the last provision() call.\n */\n async stopIdle(maxIdleMs: number): Promise<void> {\n const now = Date.now();\n const toStop: string[] = [];\n for (const [vaultId, containerState] of this.state) {\n if (containerState.status === \"running\" && now - containerState.lastUsed > maxIdleMs) {\n toStop.push(vaultId);\n }\n }\n await Promise.all(toStop.map((vaultId) => this.stop(vaultId)));\n }\n\n /**\n * Rebuild in-memory state from existing Docker containers managed by mama image mode.\n * Supports both new labeled containers and legacy name-prefixed containers.\n */\n async reconcile(): Promise<void> {\n const discovered = new Set<string>();\n const labeledNames = await this.listContainerNamesByLabel();\n for (const name of labeledNames) discovered.add(name);\n const legacyNames = await this.listContainerNamesByPrefix();\n for (const name of legacyNames) discovered.add(name);\n\n this.state.clear();\n\n for (const containerName of discovered) {\n const details = await this.inspectContainerDetails(containerName);\n if (!details) continue;\n\n const vaultId = details.vaultId || this.vaultIdFromContainerName(containerName);\n if (!vaultId) {\n log.logWarning(`Skipping unmanaged-style container without vault id`, containerName);\n continue;\n }\n\n const status: ContainerStatus = details.running ? \"running\" : \"stopped\";\n const lastUsed = details.startedAtMs ?? Date.now();\n this.state.set(vaultId, { status, lastUsed, containerName });\n }\n\n const running = Array.from(this.state.values()).filter((s) => s.status === \"running\").length;\n const stopped = this.state.size - running;\n log.logInfo(\n `Reconciled ${this.state.size} managed containers (running=${running}, stopped=${stopped})`,\n );\n }\n\n private setState(vaultId: string, status: ContainerStatus, containerName: string): void {\n this.state.set(vaultId, { status, lastUsed: Date.now(), containerName });\n }\n\n private getContainerName(vaultId: string): string {\n return this.state.get(vaultId)?.containerName ?? DockerContainerManager.containerName(vaultId);\n }\n\n private mountArgs(mounts: ContainerMount[]): string[] {\n return mounts.flatMap((mount) => [\"-v\", this.toBindSpec(mount)]);\n }\n\n private toBindSpec(mount: ContainerMount): string {\n return `${mount.source}:${mount.target}`;\n }\n\n private async runContainer(\n vaultId: string,\n containerName: string,\n mounts: ContainerMount[],\n ): Promise<void> {\n log.logInfo(`Creating container ${containerName} from image ${this.image}`);\n await this.execFileImpl(\"docker\", [\n \"run\",\n \"-d\",\n \"--name\",\n containerName,\n \"--label\",\n DockerContainerManager.MANAGED_LABEL,\n \"--label\",\n DockerContainerManager.IMAGE_MODE_LABEL,\n \"--label\",\n `${DockerContainerManager.VAULT_ID_LABEL_KEY}=${vaultId}`,\n \"-v\",\n `${this.workspaceDir}:/workspace`,\n ...this.mountArgs(mounts),\n this.image,\n \"sleep\",\n \"infinity\",\n ]);\n }\n\n private async hasBindMountDrift(\n containerName: string,\n mounts: ContainerMount[],\n ): Promise<boolean> {\n const expected = this.expectedBinds(mounts);\n const actual = await this.inspectBindMounts(containerName);\n return !this.sameBinds(expected, actual);\n }\n\n private expectedBinds(mounts: ContainerMount[]): string[] {\n return [`${this.workspaceDir}:/workspace`, ...mounts.map((mount) => this.toBindSpec(mount))]\n .slice()\n .sort();\n }\n\n private sameBinds(expected: string[], actual: string[]): boolean {\n if (expected.length !== actual.length) {\n return false;\n }\n\n return expected.every((bind, index) => bind === actual[index]);\n }\n\n private async inspectBindMounts(containerName: string): Promise<string[]> {\n const { stdout } = await this.execFileImpl(\"docker\", [\n \"inspect\",\n \"-f\",\n \"{{json .HostConfig.Binds}}\",\n containerName,\n ]);\n const payload = stdout.trim();\n const parsed = JSON.parse(payload.length > 0 ? payload : \"null\") as unknown;\n\n if (parsed === null) {\n return [];\n }\n\n if (!Array.isArray(parsed) || parsed.some((bind) => typeof bind !== \"string\")) {\n throw new Error(`Unexpected docker bind mount payload for container \"${containerName}\"`);\n }\n\n return [...parsed].sort();\n }\n\n private async inspectStatus(containerName: string): Promise<ContainerStatus> {\n try {\n const { stdout } = await this.execFileImpl(\"docker\", [\n \"inspect\",\n \"-f\",\n \"{{.State.Running}}\",\n containerName,\n ]);\n return stdout.trim() === \"true\" ? \"running\" : \"stopped\";\n } catch {\n return \"missing\";\n }\n }\n\n private async listContainerNamesByLabel(): Promise<string[]> {\n try {\n const { stdout } = await this.execFileImpl(\"docker\", [\n \"ps\",\n \"-a\",\n \"--filter\",\n `label=${DockerContainerManager.MANAGED_LABEL}`,\n \"--filter\",\n `label=${DockerContainerManager.IMAGE_MODE_LABEL}`,\n \"--format\",\n \"{{.Names}}\",\n ]);\n return this.parseNameLines(stdout);\n } catch (err) {\n log.logWarning(\n \"Failed to list labeled managed containers\",\n err instanceof Error ? err.message : String(err),\n );\n return [];\n }\n }\n\n private async listContainerNamesByPrefix(): Promise<string[]> {\n try {\n const { stdout } = await this.execFileImpl(\"docker\", [\n \"ps\",\n \"-a\",\n \"--filter\",\n `name=${DockerContainerManager.containerName(\"\")}`,\n \"--format\",\n \"{{.Names}}\",\n ]);\n return this.parseNameLines(stdout);\n } catch (err) {\n log.logWarning(\n \"Failed to list legacy managed containers\",\n err instanceof Error ? err.message : String(err),\n );\n return [];\n }\n }\n\n private parseNameLines(stdout: string): string[] {\n return stdout\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter((line) => line.length > 0);\n }\n\n private async inspectContainerDetails(\n containerName: string,\n ): Promise<{ running: boolean; startedAtMs?: number; vaultId?: string } | undefined> {\n try {\n const { stdout } = await this.execFileImpl(\"docker\", [\n \"inspect\",\n \"-f\",\n `{{.State.Running}}\\t{{.State.StartedAt}}\\t{{index .Config.Labels \"${DockerContainerManager.VAULT_ID_LABEL_KEY}\"}}`,\n containerName,\n ]);\n const [runningRaw, startedAtRaw, vaultIdRaw] = stdout.trim().split(\"\\t\");\n const running = runningRaw === \"true\";\n const startedAtMs = this.parseDockerTimestamp(startedAtRaw);\n const vaultId = this.normalizeDockerValue(vaultIdRaw);\n return { running, startedAtMs, vaultId };\n } catch (err) {\n log.logWarning(\n `Failed to inspect container ${containerName} during reconcile`,\n err instanceof Error ? err.message : String(err),\n );\n return undefined;\n }\n }\n\n private normalizeDockerValue(value?: string): string | undefined {\n if (!value || value === \"<no value>\") return undefined;\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n }\n\n private parseDockerTimestamp(value?: string): number | undefined {\n const normalized = this.normalizeDockerValue(value);\n if (!normalized || normalized.startsWith(\"0001-\")) return undefined;\n const parsed = Date.parse(normalized);\n return Number.isNaN(parsed) ? undefined : parsed;\n }\n\n private vaultIdFromContainerName(containerName: string): string | undefined {\n const prefix = DockerContainerManager.containerName(\"\");\n if (!containerName.startsWith(prefix)) return undefined;\n const vaultId = containerName.slice(prefix.length);\n return vaultId.length > 0 ? vaultId : undefined;\n }\n}\n\n/** @deprecated Use DockerContainerManager */\nexport const DockerProvisioner = DockerContainerManager;\n"]}
|
|
1
|
+
{"version":3,"file":"provisioner.js","sourceRoot":"","sources":["../src/provisioner.ts"],"names":[],"mappings":"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","sourcesContent":["import { execFile } from \"child_process\";\nimport { createHash } from \"crypto\";\nimport { readFileSync, statSync } from \"fs\";\nimport { promisify } from \"util\";\nimport * as log from \"./log.js\";\n\nconst execFileAsync = promisify(execFile);\ntype ExecFileAsync = typeof execFileAsync;\n\ntype ContainerStatus = \"running\" | \"stopped\" | \"missing\";\n\nfunction isDockerNotFoundError(err: unknown): boolean {\n if (!err || typeof err !== \"object\") return false;\n const stderr = (err as { stderr?: unknown }).stderr;\n const message = (err as { message?: unknown }).message;\n const haystack = `${typeof stderr === \"string\" ? stderr : \"\"}\\n${\n typeof message === \"string\" ? message : \"\"\n }`.toLowerCase();\n return (\n haystack.includes(\"no such network\") ||\n haystack.includes(\"no such container\") ||\n haystack.includes(\"no such object\") ||\n haystack.includes(\"network not found\") ||\n /network [^\\n]+ not found/.test(haystack) ||\n /error: no such [^\\n]+/.test(haystack)\n );\n}\n\ninterface ContainerState {\n status: ContainerStatus;\n lastUsed: number;\n containerName: string;\n}\n\nexport interface ContainerMount {\n source: string;\n target: string;\n}\n\nexport interface ResourceLimits {\n cpus?: string;\n memory?: string;\n}\n\nexport interface SandboxLimitStatus {\n limits?: ResourceLimits;\n boosted: boolean;\n}\n\nexport interface ProvisionOptions {\n containerName?: string;\n mounts?: ContainerMount[];\n conversationId?: string;\n}\n\nexport interface DockerContainerManagerOptions {\n limits?: ResourceLimits;\n boostLimits?: ResourceLimits;\n execFileImpl?: ExecFileAsync;\n}\n\nexport class DockerContainerManager {\n private state = new Map<string, ContainerState>();\n private inflight = new Map<string, Promise<string>>();\n private static readonly MANAGED_LABEL = \"mama.managed=true\";\n private static readonly IMAGE_MODE_LABEL = \"mama.sandbox=image\";\n private static readonly VAULT_ID_LABEL_KEY = \"mama.vault-id\";\n private static readonly CONVERSATION_ID_LABEL_KEY = \"mama.conversation-id\";\n private static readonly MOUNT_SIGNATURE_LABEL_KEY = \"mama.mount-signature\";\n\n private readonly limits?: ResourceLimits;\n private readonly boostLimits?: ResourceLimits;\n private readonly boostedKeys = new Set<string>();\n private readonly execFileImpl: ExecFileAsync;\n\n constructor(\n private readonly image: string,\n options: DockerContainerManagerOptions | ExecFileAsync = {},\n ) {\n if (typeof options === \"function\") {\n this.execFileImpl = options;\n } else {\n this.limits = options.limits;\n this.boostLimits = options.boostLimits;\n this.execFileImpl = options.execFileImpl ?? execFileAsync;\n }\n }\n\n static sanitizeSegment(value: string): string {\n const sanitized = value\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n return sanitized || \"unknown\";\n }\n\n static containerName(containerKey: string): string {\n return `mama-sandbox-${containerKey}`;\n }\n\n static networkName(containerKey: string): string {\n return `mama-sandbox-net-${containerKey}`;\n }\n\n async provision(containerKey: string, options: ProvisionOptions = {}): Promise<string> {\n const existing = this.inflight.get(containerKey);\n if (existing) return existing;\n\n const pending = this.provisionInner(containerKey, options).finally(() => {\n this.inflight.delete(containerKey);\n });\n this.inflight.set(containerKey, pending);\n return pending;\n }\n\n private async provisionInner(containerKey: string, options: ProvisionOptions): Promise<string> {\n const containerName =\n options.containerName ?? DockerContainerManager.containerName(containerKey);\n const mounts = options.mounts ?? [];\n const status = await this.inspectStatus(containerName);\n\n try {\n if (\n status !== \"missing\" &&\n (await this.hasRuntimeDrift(containerKey, containerName, mounts))\n ) {\n log.logInfo(`Container ${containerName} configuration changed; recreating container`);\n await this.execFileImpl(\"docker\", [\"rm\", \"-f\", containerName]);\n await this.runContainer(containerKey, containerName, mounts, options);\n log.logInfo(`Container ${containerName} recreated`);\n } else if (status === \"running\") {\n log.logInfo(`Container ${containerName} already running`);\n } else if (status === \"stopped\") {\n await this.execFileImpl(\"docker\", [\"start\", containerName]);\n log.logInfo(`Container ${containerName} started`);\n } else {\n await this.runContainer(containerKey, containerName, mounts, options);\n log.logInfo(`Container ${containerName} created`);\n }\n } catch (err) {\n this.state.delete(containerKey);\n throw err;\n }\n\n this.setState(containerKey, \"running\", containerName);\n await this.applyResourceLimits(containerKey, containerName);\n return containerName;\n }\n\n async boost(containerKey: string): Promise<SandboxLimitStatus> {\n if (!this.boostLimits?.cpus && !this.boostLimits?.memory) {\n return this.getLimitStatus(containerKey);\n }\n\n this.boostedKeys.add(containerKey);\n const state = this.state.get(containerKey);\n if (state?.status === \"running\") {\n await this.applyResourceLimits(containerKey, state.containerName);\n }\n return this.getLimitStatus(containerKey);\n }\n\n getLimitStatus(containerKey: string): SandboxLimitStatus {\n const boosted = this.boostedKeys.has(containerKey);\n return { limits: this.effectiveLimits(containerKey), boosted };\n }\n\n getDefaultLimits(): ResourceLimits | undefined {\n return this.limits;\n }\n\n getBoostLimits(): ResourceLimits | undefined {\n return this.boostLimits;\n }\n\n async stop(containerKey: string): Promise<void> {\n const containerName = this.getContainerName(containerKey);\n try {\n await this.execFileImpl(\"docker\", [\"stop\", containerName]);\n this.setState(containerKey, \"stopped\", containerName);\n this.boostedKeys.delete(containerKey);\n log.logInfo(`Container ${containerName} stopped (idle)`);\n } catch (err) {\n log.logWarning(\n `Failed to stop container ${containerName}`,\n err instanceof Error ? err.message : String(err),\n );\n }\n }\n\n async remove(containerKey: string): Promise<void> {\n const containerName = this.getContainerName(containerKey);\n const networkName = DockerContainerManager.networkName(containerKey);\n\n await this.forceRemoveContainer(\n containerName,\n `Container ${containerName} removed`,\n `Failed to remove container ${containerName}`,\n );\n\n try {\n await this.execFileImpl(\"docker\", [\"network\", \"rm\", networkName]);\n log.logInfo(`Network ${networkName} removed`);\n } catch (err) {\n log.logWarning(\n `Failed to remove network ${networkName}`,\n err instanceof Error ? err.message : String(err),\n );\n }\n\n this.state.delete(containerKey);\n this.boostedKeys.delete(containerKey);\n }\n\n async stopIdle(maxIdleMs: number): Promise<void> {\n const now = Date.now();\n const toStop: string[] = [];\n for (const [containerKey, containerState] of this.state) {\n if (containerState.status === \"running\" && now - containerState.lastUsed > maxIdleMs) {\n toStop.push(containerKey);\n }\n }\n await Promise.all(toStop.map((containerKey) => this.stop(containerKey)));\n }\n\n async reconcile(): Promise<void> {\n const discovered = new Set<string>();\n const labeledNames = await this.listContainerNamesByLabel();\n for (const name of labeledNames) discovered.add(name);\n const legacyNames = await this.listContainerNamesByPrefix();\n for (const name of legacyNames) discovered.add(name);\n\n this.state.clear();\n\n const inspected = await Promise.all(\n Array.from(discovered).map(async (containerName) => ({\n containerName,\n details: await this.inspectContainerDetails(containerName),\n })),\n );\n\n const legacyRemovals: Promise<void>[] = [];\n for (const { containerName, details } of inspected) {\n if (!details) continue;\n\n if (!details.conversationId) {\n legacyRemovals.push(this.removeLegacyContainer(containerName));\n continue;\n }\n\n const containerKey = this.containerKeyFromContainerName(containerName);\n if (!containerKey) {\n log.logWarning(`Skipping unmanaged-style container without container key`, containerName);\n continue;\n }\n\n const status: ContainerStatus = details.running ? \"running\" : \"stopped\";\n const lastUsed = details.startedAtMs ?? Date.now();\n this.state.set(containerKey, { status, lastUsed, containerName });\n }\n await Promise.all(legacyRemovals);\n\n const running = Array.from(this.state.values()).filter((s) => s.status === \"running\").length;\n const stopped = this.state.size - running;\n log.logInfo(\n `Reconciled ${this.state.size} managed containers (running=${running}, stopped=${stopped})`,\n );\n }\n\n private setState(containerKey: string, status: ContainerStatus, containerName: string): void {\n this.state.set(containerKey, { status, lastUsed: Date.now(), containerName });\n }\n\n private getContainerName(containerKey: string): string {\n return (\n this.state.get(containerKey)?.containerName ??\n DockerContainerManager.containerName(containerKey)\n );\n }\n\n private mountArgs(mounts: ContainerMount[]): string[] {\n return mounts.flatMap((mount) => [\"-v\", this.toBindSpec(mount)]);\n }\n\n private toBindSpec(mount: ContainerMount): string {\n return `${mount.source}:${mount.target}`;\n }\n\n private async runContainer(\n containerKey: string,\n containerName: string,\n mounts: ContainerMount[],\n options: ProvisionOptions,\n ): Promise<void> {\n const networkName = await this.ensureNetwork(containerKey);\n log.logInfo(`Creating container ${containerName} from image ${this.image}`);\n const labels = [\n \"--label\",\n DockerContainerManager.MANAGED_LABEL,\n \"--label\",\n DockerContainerManager.IMAGE_MODE_LABEL,\n \"--label\",\n `${DockerContainerManager.VAULT_ID_LABEL_KEY}=${containerKey}`,\n ];\n if (options.conversationId) {\n labels.push(\n \"--label\",\n `${DockerContainerManager.CONVERSATION_ID_LABEL_KEY}=${options.conversationId}`,\n );\n }\n if (mounts.length > 0) {\n labels.push(\n \"--label\",\n `${DockerContainerManager.MOUNT_SIGNATURE_LABEL_KEY}=${this.mountSignature(mounts)}`,\n );\n }\n await this.execFileImpl(\"docker\", [\n \"run\",\n \"-d\",\n \"--name\",\n containerName,\n \"--network\",\n networkName,\n ...labels,\n ...this.resourceLimitArgs(this.effectiveLimits(containerKey)),\n ...this.mountArgs(mounts),\n this.image,\n \"sleep\",\n \"infinity\",\n ]);\n }\n\n private effectiveLimits(containerKey: string): ResourceLimits | undefined {\n if (!this.boostedKeys.has(containerKey)) return this.limits;\n return { ...this.limits, ...this.boostLimits };\n }\n\n private resourceLimitArgs(limits: ResourceLimits | undefined): string[] {\n const args: string[] = [];\n if (limits?.cpus) args.push(\"--cpus\", limits.cpus);\n if (limits?.memory) args.push(\"--memory\", limits.memory);\n return args;\n }\n\n private async applyResourceLimits(containerKey: string, containerName: string): Promise<void> {\n const limitArgs = this.resourceLimitArgs(this.effectiveLimits(containerKey));\n if (limitArgs.length === 0) return;\n const args = [\"update\", ...limitArgs, containerName];\n try {\n await this.execFileImpl(\"docker\", args);\n } catch (err) {\n log.logWarning(\n `Failed to apply resource limits to container ${containerName}`,\n err instanceof Error ? err.message : String(err),\n );\n }\n }\n\n private async hasRuntimeDrift(\n containerKey: string,\n containerName: string,\n mounts: ContainerMount[],\n ): Promise<boolean> {\n if (await this.hasBindMountDrift(containerName, mounts)) {\n return true;\n }\n if (await this.hasMountSignatureDrift(containerName, mounts)) {\n return true;\n }\n return this.hasNetworkModeDrift(containerKey, containerName);\n }\n\n private async hasBindMountDrift(\n containerName: string,\n mounts: ContainerMount[],\n ): Promise<boolean> {\n const expected = this.expectedBinds(mounts);\n const actual = await this.inspectBindMounts(containerName);\n return !this.sameBinds(expected, actual);\n }\n\n private expectedBinds(mounts: ContainerMount[]): string[] {\n return mounts\n .map((mount) => this.toBindSpec(mount))\n .slice()\n .toSorted();\n }\n\n private sameBinds(expected: string[], actual: string[]): boolean {\n if (expected.length !== actual.length) {\n return false;\n }\n\n return expected.every((bind, index) => bind === actual[index]);\n }\n\n private async hasMountSignatureDrift(\n containerName: string,\n mounts: ContainerMount[],\n ): Promise<boolean> {\n if (mounts.length === 0) return false;\n const expected = this.mountSignature(mounts);\n const { stdout } = await this.execFileImpl(\"docker\", [\n \"inspect\",\n \"-f\",\n `{{index .Config.Labels \"${DockerContainerManager.MOUNT_SIGNATURE_LABEL_KEY}\"}}`,\n containerName,\n ]);\n const actual = this.normalizeDockerValue(stdout.trim());\n return actual !== expected;\n }\n\n private mountSignature(mounts: ContainerMount[]): string {\n const payload = mounts\n .map((mount) => ({\n source: mount.source,\n target: mount.target,\n fingerprint: this.mountSourceFingerprint(mount.source),\n }))\n .toSorted((left, right) =>\n `${left.target}\\0${left.source}`.localeCompare(`${right.target}\\0${right.source}`),\n );\n return createHash(\"sha256\").update(JSON.stringify(payload)).digest(\"hex\");\n }\n\n private mountSourceFingerprint(source: string): string {\n try {\n const stat = statSync(source);\n if (stat.isFile()) {\n return createHash(\"sha256\").update(readFileSync(source)).digest(\"hex\");\n }\n return `${stat.isDirectory() ? \"dir\" : \"other\"}:${stat.size}:${stat.mtimeMs}`;\n } catch {\n return \"missing\";\n }\n }\n\n private async inspectBindMounts(containerName: string): Promise<string[]> {\n const { stdout } = await this.execFileImpl(\"docker\", [\n \"inspect\",\n \"-f\",\n \"{{json .HostConfig.Binds}}\",\n containerName,\n ]);\n const payload = stdout.trim();\n const parsed = JSON.parse(payload.length > 0 ? payload : \"null\") as unknown;\n\n if (parsed === null) {\n return [];\n }\n\n if (!Array.isArray(parsed) || parsed.some((bind) => typeof bind !== \"string\")) {\n throw new Error(`Unexpected docker bind mount payload for container \"${containerName}\"`);\n }\n\n return [...parsed].toSorted();\n }\n\n private async hasNetworkModeDrift(containerKey: string, containerName: string): Promise<boolean> {\n const expected = DockerContainerManager.networkName(containerKey);\n const { stdout } = await this.execFileImpl(\"docker\", [\n \"inspect\",\n \"-f\",\n \"{{.HostConfig.NetworkMode}}\",\n containerName,\n ]);\n return stdout.trim() !== expected;\n }\n\n private async ensureNetwork(containerKey: string): Promise<string> {\n const networkName = DockerContainerManager.networkName(containerKey);\n try {\n await this.execFileImpl(\"docker\", [\"network\", \"inspect\", networkName]);\n return networkName;\n } catch (err) {\n if (!isDockerNotFoundError(err)) throw err;\n }\n await this.execFileImpl(\"docker\", [\n \"network\",\n \"create\",\n \"--driver\",\n \"bridge\",\n \"--label\",\n DockerContainerManager.MANAGED_LABEL,\n \"--label\",\n DockerContainerManager.IMAGE_MODE_LABEL,\n \"--label\",\n `${DockerContainerManager.VAULT_ID_LABEL_KEY}=${containerKey}`,\n networkName,\n ]);\n return networkName;\n }\n\n private async inspectStatus(containerName: string): Promise<ContainerStatus> {\n try {\n const { stdout } = await this.execFileImpl(\"docker\", [\n \"inspect\",\n \"-f\",\n \"{{.State.Running}}\",\n containerName,\n ]);\n return stdout.trim() === \"true\" ? \"running\" : \"stopped\";\n } catch (err) {\n if (isDockerNotFoundError(err)) return \"missing\";\n throw err;\n }\n }\n\n private async listContainerNamesByLabel(): Promise<string[]> {\n try {\n const { stdout } = await this.execFileImpl(\"docker\", [\n \"ps\",\n \"-a\",\n \"--filter\",\n `label=${DockerContainerManager.MANAGED_LABEL}`,\n \"--filter\",\n `label=${DockerContainerManager.IMAGE_MODE_LABEL}`,\n \"--format\",\n \"{{.Names}}\",\n ]);\n return this.parseNameLines(stdout);\n } catch (err) {\n log.logWarning(\n \"Failed to list labeled managed containers\",\n err instanceof Error ? err.message : String(err),\n );\n return [];\n }\n }\n\n private async listContainerNamesByPrefix(): Promise<string[]> {\n try {\n const { stdout } = await this.execFileImpl(\"docker\", [\n \"ps\",\n \"-a\",\n \"--filter\",\n `name=${DockerContainerManager.containerName(\"\")}`,\n \"--format\",\n \"{{.Names}}\",\n ]);\n return this.parseNameLines(stdout);\n } catch (err) {\n log.logWarning(\n \"Failed to list legacy managed containers\",\n err instanceof Error ? err.message : String(err),\n );\n return [];\n }\n }\n\n private parseNameLines(stdout: string): string[] {\n return stdout\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter((line) => line.length > 0);\n }\n\n private async inspectContainerDetails(\n containerName: string,\n ): Promise<\n | { running: boolean; startedAtMs?: number; vaultId?: string; conversationId?: string }\n | undefined\n > {\n try {\n const { stdout } = await this.execFileImpl(\"docker\", [\n \"inspect\",\n \"-f\",\n `{{.State.Running}}\\t{{.State.StartedAt}}\\t{{index .Config.Labels \"${DockerContainerManager.VAULT_ID_LABEL_KEY}\"}}\\t{{index .Config.Labels \"${DockerContainerManager.CONVERSATION_ID_LABEL_KEY}\"}}`,\n containerName,\n ]);\n const [runningRaw, startedAtRaw, vaultIdRaw, conversationIdRaw] = stdout.trim().split(\"\\t\");\n const running = runningRaw === \"true\";\n const startedAtMs = this.parseDockerTimestamp(startedAtRaw);\n const vaultId = this.normalizeDockerValue(vaultIdRaw);\n const conversationId = this.normalizeDockerValue(conversationIdRaw);\n return { running, startedAtMs, vaultId, conversationId };\n } catch (err) {\n log.logWarning(\n `Failed to inspect container ${containerName} during reconcile`,\n err instanceof Error ? err.message : String(err),\n );\n return undefined;\n }\n }\n\n private normalizeDockerValue(value?: string): string | undefined {\n if (!value || value === \"<no value>\") return undefined;\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n }\n\n private parseDockerTimestamp(value?: string): number | undefined {\n const normalized = this.normalizeDockerValue(value);\n if (!normalized || normalized.startsWith(\"0001-\")) return undefined;\n const parsed = Date.parse(normalized);\n return Number.isNaN(parsed) ? undefined : parsed;\n }\n\n private containerKeyFromContainerName(containerName: string): string | undefined {\n const prefix = DockerContainerManager.containerName(\"\");\n if (!containerName.startsWith(prefix)) return undefined;\n const containerKey = containerName.slice(prefix.length);\n return containerKey.length > 0 ? containerKey : undefined;\n }\n\n private async forceRemoveContainer(\n containerName: string,\n successLog: string,\n failureLog: string,\n ): Promise<void> {\n try {\n await this.execFileImpl(\"docker\", [\"rm\", \"-f\", containerName]);\n log.logInfo(successLog);\n } catch (err) {\n log.logWarning(failureLog, err instanceof Error ? err.message : String(err));\n }\n }\n\n private async removeLegacyContainer(containerName: string): Promise<void> {\n await this.forceRemoveContainer(\n containerName,\n `Removed legacy mama container ${containerName} (pre-channel-isolation scheme)`,\n `Failed to remove legacy mama container ${containerName}`,\n );\n }\n}\n"]}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Bot, BotAdapters, BotEvent } from "../adapter.js";
|
|
2
|
+
import type { AgentRunner } from "../agent.js";
|
|
3
|
+
import type { CommandRegistry } from "../commands/index.js";
|
|
4
|
+
import type { CommandServices } from "../commands/index.js";
|
|
5
|
+
export interface ConversationRuntimeState {
|
|
6
|
+
running: boolean;
|
|
7
|
+
runner: AgentRunner;
|
|
8
|
+
stopRequested: boolean;
|
|
9
|
+
stopMessageTs?: string;
|
|
10
|
+
lastAccessedAt: number;
|
|
11
|
+
startedAt?: number;
|
|
12
|
+
lastActivityAt?: number;
|
|
13
|
+
}
|
|
14
|
+
export interface RunConversationOptions {
|
|
15
|
+
event: BotEvent;
|
|
16
|
+
bot: Bot;
|
|
17
|
+
adapters: BotAdapters;
|
|
18
|
+
isSyntheticEvent?: boolean;
|
|
19
|
+
}
|
|
20
|
+
interface ConversationOrchestratorOptions {
|
|
21
|
+
workingDir: string;
|
|
22
|
+
commandRegistry: CommandRegistry;
|
|
23
|
+
commandServices: CommandServices;
|
|
24
|
+
isShuttingDown: () => boolean;
|
|
25
|
+
getState: (sessionKey: string) => ConversationRuntimeState | undefined;
|
|
26
|
+
getOrCreateState: (options: {
|
|
27
|
+
conversationId: string;
|
|
28
|
+
platformName: string;
|
|
29
|
+
sessionKey: string;
|
|
30
|
+
}) => Promise<ConversationRuntimeState>;
|
|
31
|
+
beforeRunTracked: (runPromise: Promise<void>) => void;
|
|
32
|
+
afterRunTracked: (runPromise: Promise<void>) => void;
|
|
33
|
+
onRunFinished: () => void;
|
|
34
|
+
}
|
|
35
|
+
export declare class ConversationOrchestrator {
|
|
36
|
+
private readonly options;
|
|
37
|
+
constructor(options: ConversationOrchestratorOptions);
|
|
38
|
+
runSession({ event, bot, adapters, isSyntheticEvent }: RunConversationOptions): Promise<void>;
|
|
39
|
+
private runWithInstrumentation;
|
|
40
|
+
}
|
|
41
|
+
export {};
|
|
42
|
+
//# sourceMappingURL=conversation-orchestrator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversation-orchestrator.d.ts","sourceRoot":"","sources":["../../src/runtime/conversation-orchestrator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,WAAW,EAAE,QAAQ,EAAgB,MAAM,eAAe,CAAC;AAK9E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAQ5D,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,WAAW,CAAC;IACpB,aAAa,EAAE,OAAO,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,QAAQ,CAAC;IAChB,GAAG,EAAE,GAAG,CAAC;IACT,QAAQ,EAAE,WAAW,CAAC;IACtB,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,UAAU,+BAA+B;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,eAAe,CAAC;IACjC,eAAe,EAAE,eAAe,CAAC;IACjC,cAAc,EAAE,MAAM,OAAO,CAAC;IAC9B,QAAQ,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,wBAAwB,GAAG,SAAS,CAAC;IACvE,gBAAgB,EAAE,CAAC,OAAO,EAAE;QAC1B,cAAc,EAAE,MAAM,CAAC;QACvB,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,MAAM,CAAC;KACpB,KAAK,OAAO,CAAC,wBAAwB,CAAC,CAAC;IACxC,gBAAgB,EAAE,CAAC,UAAU,EAAE,OAAO,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC;IACtD,eAAe,EAAE,CAAC,UAAU,EAAE,OAAO,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC;IACrD,aAAa,EAAE,MAAM,IAAI,CAAC;CAC3B;AAED,qBAAa,wBAAwB;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAApC,YAA6B,OAAO,EAAE,+BAA+B,EAAI;IAEnE,UAAU,CAAC,EACf,KAAK,EACL,GAAG,EACH,QAAQ,EACR,gBAAgB,EACjB,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CA6FxC;YAEa,sBAAsB;CA8ErC","sourcesContent":["import type { Bot, BotAdapters, BotEvent, PlatformName } from \"../adapter.js\";\nimport {\n hasMaterializedSlackBranchSession,\n waitForSlackBranchBootstrap,\n} from \"../adapters/slack/branch-manager.js\";\nimport type { AgentRunner } from \"../agent.js\";\nimport type { CommandRegistry } from \"../commands/index.js\";\nimport type { CommandServices } from \"../commands/index.js\";\nimport { isPrivateConversation } from \"../commands/utils.js\";\nimport * as log from \"../log.js\";\nimport { addLifecycleBreadcrumb, applyRunScope } from \"../sentry.js\";\nimport { formatStopped } from \"../ui-copy.js\";\nimport * as Sentry from \"@sentry/node\";\nimport { join } from \"path\";\n\nexport interface ConversationRuntimeState {\n running: boolean;\n runner: AgentRunner;\n stopRequested: boolean;\n stopMessageTs?: string;\n lastAccessedAt: number;\n startedAt?: number;\n lastActivityAt?: number;\n}\n\nexport interface RunConversationOptions {\n event: BotEvent;\n bot: Bot;\n adapters: BotAdapters;\n isSyntheticEvent?: boolean;\n}\n\ninterface ConversationOrchestratorOptions {\n workingDir: string;\n commandRegistry: CommandRegistry;\n commandServices: CommandServices;\n isShuttingDown: () => boolean;\n getState: (sessionKey: string) => ConversationRuntimeState | undefined;\n getOrCreateState: (options: {\n conversationId: string;\n platformName: string;\n sessionKey: string;\n }) => Promise<ConversationRuntimeState>;\n beforeRunTracked: (runPromise: Promise<void>) => void;\n afterRunTracked: (runPromise: Promise<void>) => void;\n onRunFinished: () => void;\n}\n\nexport class ConversationOrchestrator {\n constructor(private readonly options: ConversationOrchestratorOptions) {}\n\n async runSession({\n event,\n bot,\n adapters,\n isSyntheticEvent,\n }: RunConversationOptions): Promise<void> {\n const conversationId = event.conversationId;\n if (this.options.isShuttingDown()) {\n log.logInfo(\n `[${conversationId}] Rejected event during shutdown: ${event.text.substring(0, 50)}`,\n );\n return;\n }\n\n const sessionKey = event.sessionKey ?? `${conversationId}:${event.thread_ts ?? event.ts}`;\n const privateConversation = isPrivateConversation(event);\n const handledCommand = await this.options.commandRegistry.handle({\n bot,\n responseCtx: adapters.responseCtx,\n platform: adapters.platform.name as PlatformName,\n platformUserId: event.user,\n conversationId,\n vaultConversationId: event.vaultConversationId,\n sessionKey,\n commandText: event.text,\n privateConversation,\n services: this.options.commandServices,\n });\n if (handledCommand) return;\n\n const conversationDir = join(this.options.workingDir, conversationId);\n const waitedForParent =\n adapters.platform.name === \"slack\" && !isSyntheticEvent\n ? await waitForSlackBranchBootstrap({\n parentSessionKey: conversationId,\n sessionKey,\n hasThreadSession: () => hasMaterializedSlackBranchSession(conversationDir, sessionKey),\n isParentRunning: () => this.options.getState(conversationId)?.running === true,\n })\n : false;\n if (waitedForParent) {\n log.logInfo(\n `[${conversationId}] Delayed thread bootstrap until parent session sealed: ${sessionKey}`,\n );\n }\n\n const state = await this.options.getOrCreateState({\n conversationId,\n platformName: adapters.platform.name,\n sessionKey,\n });\n\n state.running = true;\n state.stopRequested = false;\n state.startedAt = Date.now();\n state.lastActivityAt = Date.now();\n\n log.logInfo(`[${conversationId}] Starting run: ${event.text.substring(0, 50)}`);\n\n const runPromise = (async () => {\n try {\n const result = await this.runWithInstrumentation(\n adapters,\n { conversationId, sessionKey, isSyntheticEvent, startedAt: state.startedAt! },\n async () => {\n await adapters.responseCtx.setTyping(true);\n await adapters.responseCtx.setWorking(true);\n const runnerResult = await state.runner.run(\n adapters.message,\n adapters.responseCtx,\n adapters.platform,\n );\n await adapters.responseCtx.setWorking(false);\n return runnerResult;\n },\n );\n\n if (result?.stopReason === \"aborted\" && state.stopRequested) {\n if (state.stopMessageTs) {\n await bot.updateMessage(conversationId, state.stopMessageTs, formatStopped(bot));\n state.stopMessageTs = undefined;\n } else {\n await bot.postMessage(conversationId, formatStopped(bot));\n }\n }\n } finally {\n state.running = false;\n state.lastAccessedAt = Date.now();\n this.options.onRunFinished();\n }\n })();\n\n this.options.beforeRunTracked(runPromise);\n try {\n await runPromise;\n } finally {\n this.options.afterRunTracked(runPromise);\n }\n }\n\n private async runWithInstrumentation(\n adapters: BotAdapters,\n meta: {\n conversationId: string;\n sessionKey: string;\n isSyntheticEvent?: boolean;\n startedAt: number;\n },\n body: () => Promise<{ stopReason: string; errorMessage?: string }>,\n ): Promise<{ stopReason: string; errorMessage?: string } | undefined> {\n const { conversationId, sessionKey, isSyntheticEvent, startedAt } = meta;\n const { message, platform } = adapters;\n\n Sentry.metrics.count(\"agent.run.started\", 1, {\n attributes: { channel: conversationId },\n });\n\n return Sentry.startSpan(\n { name: \"agent.run\", op: \"agent\", attributes: { conversationId, sessionKey } },\n async () =>\n Sentry.withScope(async (scope) => {\n applyRunScope(scope, {\n conversationId,\n sessionKey,\n messageId: message.id,\n platform: platform.name,\n userId: message.userId,\n userName: message.userName,\n threadTs: message.threadTs,\n isSyntheticEvent,\n });\n addLifecycleBreadcrumb(\"agent.run.started\", {\n channel_id: conversationId,\n platform: platform.name,\n has_attachments: (message.attachments?.length ?? 0) > 0,\n });\n\n try {\n const result = await body();\n const durationMs = Date.now() - startedAt;\n const completionAttrs = {\n channel: conversationId,\n platform: platform.name,\n stop_reason: result.stopReason,\n };\n Sentry.metrics.distribution(\"agent.run.duration\", durationMs, {\n unit: \"millisecond\",\n attributes: completionAttrs,\n });\n Sentry.metrics.count(\"agent.run.completed\", 1, { attributes: completionAttrs });\n addLifecycleBreadcrumb(\"agent.run.completed\", {\n channel_id: conversationId,\n platform: platform.name,\n stop_reason: result.stopReason,\n duration_ms: durationMs,\n });\n return result;\n } catch (err) {\n scope.setContext(\"agent_run_error\", {\n conversationId,\n sessionKey,\n platform: platform.name,\n messageId: message.id,\n threadTs: message.threadTs,\n });\n Sentry.captureException(err);\n Sentry.metrics.count(\"agent.run.errors\", 1, {\n attributes: { channel: conversationId, platform: platform.name },\n });\n log.logWarning(\n `[${conversationId}] Run error`,\n err instanceof Error ? err.message : String(err),\n );\n return undefined;\n }\n }),\n );\n }\n}\n"]}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { hasMaterializedSlackBranchSession, waitForSlackBranchBootstrap, } from "../adapters/slack/branch-manager.js";
|
|
2
|
+
import { isPrivateConversation } from "../commands/utils.js";
|
|
3
|
+
import * as log from "../log.js";
|
|
4
|
+
import { addLifecycleBreadcrumb, applyRunScope } from "../sentry.js";
|
|
5
|
+
import { formatStopped } from "../ui-copy.js";
|
|
6
|
+
import * as Sentry from "@sentry/node";
|
|
7
|
+
import { join } from "path";
|
|
8
|
+
export class ConversationOrchestrator {
|
|
9
|
+
constructor(options) {
|
|
10
|
+
this.options = options;
|
|
11
|
+
}
|
|
12
|
+
async runSession({ event, bot, adapters, isSyntheticEvent, }) {
|
|
13
|
+
const conversationId = event.conversationId;
|
|
14
|
+
if (this.options.isShuttingDown()) {
|
|
15
|
+
log.logInfo(`[${conversationId}] Rejected event during shutdown: ${event.text.substring(0, 50)}`);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const sessionKey = event.sessionKey ?? `${conversationId}:${event.thread_ts ?? event.ts}`;
|
|
19
|
+
const privateConversation = isPrivateConversation(event);
|
|
20
|
+
const handledCommand = await this.options.commandRegistry.handle({
|
|
21
|
+
bot,
|
|
22
|
+
responseCtx: adapters.responseCtx,
|
|
23
|
+
platform: adapters.platform.name,
|
|
24
|
+
platformUserId: event.user,
|
|
25
|
+
conversationId,
|
|
26
|
+
vaultConversationId: event.vaultConversationId,
|
|
27
|
+
sessionKey,
|
|
28
|
+
commandText: event.text,
|
|
29
|
+
privateConversation,
|
|
30
|
+
services: this.options.commandServices,
|
|
31
|
+
});
|
|
32
|
+
if (handledCommand)
|
|
33
|
+
return;
|
|
34
|
+
const conversationDir = join(this.options.workingDir, conversationId);
|
|
35
|
+
const waitedForParent = adapters.platform.name === "slack" && !isSyntheticEvent
|
|
36
|
+
? await waitForSlackBranchBootstrap({
|
|
37
|
+
parentSessionKey: conversationId,
|
|
38
|
+
sessionKey,
|
|
39
|
+
hasThreadSession: () => hasMaterializedSlackBranchSession(conversationDir, sessionKey),
|
|
40
|
+
isParentRunning: () => this.options.getState(conversationId)?.running === true,
|
|
41
|
+
})
|
|
42
|
+
: false;
|
|
43
|
+
if (waitedForParent) {
|
|
44
|
+
log.logInfo(`[${conversationId}] Delayed thread bootstrap until parent session sealed: ${sessionKey}`);
|
|
45
|
+
}
|
|
46
|
+
const state = await this.options.getOrCreateState({
|
|
47
|
+
conversationId,
|
|
48
|
+
platformName: adapters.platform.name,
|
|
49
|
+
sessionKey,
|
|
50
|
+
});
|
|
51
|
+
state.running = true;
|
|
52
|
+
state.stopRequested = false;
|
|
53
|
+
state.startedAt = Date.now();
|
|
54
|
+
state.lastActivityAt = Date.now();
|
|
55
|
+
log.logInfo(`[${conversationId}] Starting run: ${event.text.substring(0, 50)}`);
|
|
56
|
+
const runPromise = (async () => {
|
|
57
|
+
try {
|
|
58
|
+
const result = await this.runWithInstrumentation(adapters, { conversationId, sessionKey, isSyntheticEvent, startedAt: state.startedAt }, async () => {
|
|
59
|
+
await adapters.responseCtx.setTyping(true);
|
|
60
|
+
await adapters.responseCtx.setWorking(true);
|
|
61
|
+
const runnerResult = await state.runner.run(adapters.message, adapters.responseCtx, adapters.platform);
|
|
62
|
+
await adapters.responseCtx.setWorking(false);
|
|
63
|
+
return runnerResult;
|
|
64
|
+
});
|
|
65
|
+
if (result?.stopReason === "aborted" && state.stopRequested) {
|
|
66
|
+
if (state.stopMessageTs) {
|
|
67
|
+
await bot.updateMessage(conversationId, state.stopMessageTs, formatStopped(bot));
|
|
68
|
+
state.stopMessageTs = undefined;
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
await bot.postMessage(conversationId, formatStopped(bot));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
finally {
|
|
76
|
+
state.running = false;
|
|
77
|
+
state.lastAccessedAt = Date.now();
|
|
78
|
+
this.options.onRunFinished();
|
|
79
|
+
}
|
|
80
|
+
})();
|
|
81
|
+
this.options.beforeRunTracked(runPromise);
|
|
82
|
+
try {
|
|
83
|
+
await runPromise;
|
|
84
|
+
}
|
|
85
|
+
finally {
|
|
86
|
+
this.options.afterRunTracked(runPromise);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
async runWithInstrumentation(adapters, meta, body) {
|
|
90
|
+
const { conversationId, sessionKey, isSyntheticEvent, startedAt } = meta;
|
|
91
|
+
const { message, platform } = adapters;
|
|
92
|
+
Sentry.metrics.count("agent.run.started", 1, {
|
|
93
|
+
attributes: { channel: conversationId },
|
|
94
|
+
});
|
|
95
|
+
return Sentry.startSpan({ name: "agent.run", op: "agent", attributes: { conversationId, sessionKey } }, async () => Sentry.withScope(async (scope) => {
|
|
96
|
+
applyRunScope(scope, {
|
|
97
|
+
conversationId,
|
|
98
|
+
sessionKey,
|
|
99
|
+
messageId: message.id,
|
|
100
|
+
platform: platform.name,
|
|
101
|
+
userId: message.userId,
|
|
102
|
+
userName: message.userName,
|
|
103
|
+
threadTs: message.threadTs,
|
|
104
|
+
isSyntheticEvent,
|
|
105
|
+
});
|
|
106
|
+
addLifecycleBreadcrumb("agent.run.started", {
|
|
107
|
+
channel_id: conversationId,
|
|
108
|
+
platform: platform.name,
|
|
109
|
+
has_attachments: (message.attachments?.length ?? 0) > 0,
|
|
110
|
+
});
|
|
111
|
+
try {
|
|
112
|
+
const result = await body();
|
|
113
|
+
const durationMs = Date.now() - startedAt;
|
|
114
|
+
const completionAttrs = {
|
|
115
|
+
channel: conversationId,
|
|
116
|
+
platform: platform.name,
|
|
117
|
+
stop_reason: result.stopReason,
|
|
118
|
+
};
|
|
119
|
+
Sentry.metrics.distribution("agent.run.duration", durationMs, {
|
|
120
|
+
unit: "millisecond",
|
|
121
|
+
attributes: completionAttrs,
|
|
122
|
+
});
|
|
123
|
+
Sentry.metrics.count("agent.run.completed", 1, { attributes: completionAttrs });
|
|
124
|
+
addLifecycleBreadcrumb("agent.run.completed", {
|
|
125
|
+
channel_id: conversationId,
|
|
126
|
+
platform: platform.name,
|
|
127
|
+
stop_reason: result.stopReason,
|
|
128
|
+
duration_ms: durationMs,
|
|
129
|
+
});
|
|
130
|
+
return result;
|
|
131
|
+
}
|
|
132
|
+
catch (err) {
|
|
133
|
+
scope.setContext("agent_run_error", {
|
|
134
|
+
conversationId,
|
|
135
|
+
sessionKey,
|
|
136
|
+
platform: platform.name,
|
|
137
|
+
messageId: message.id,
|
|
138
|
+
threadTs: message.threadTs,
|
|
139
|
+
});
|
|
140
|
+
Sentry.captureException(err);
|
|
141
|
+
Sentry.metrics.count("agent.run.errors", 1, {
|
|
142
|
+
attributes: { channel: conversationId, platform: platform.name },
|
|
143
|
+
});
|
|
144
|
+
log.logWarning(`[${conversationId}] Run error`, err instanceof Error ? err.message : String(err));
|
|
145
|
+
return undefined;
|
|
146
|
+
}
|
|
147
|
+
}));
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=conversation-orchestrator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversation-orchestrator.js","sourceRoot":"","sources":["../../src/runtime/conversation-orchestrator.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Bot, BotAdapters, BotEvent, PlatformName } from \"../adapter.js\";\nimport {\n hasMaterializedSlackBranchSession,\n waitForSlackBranchBootstrap,\n} from \"../adapters/slack/branch-manager.js\";\nimport type { AgentRunner } from \"../agent.js\";\nimport type { CommandRegistry } from \"../commands/index.js\";\nimport type { CommandServices } from \"../commands/index.js\";\nimport { isPrivateConversation } from \"../commands/utils.js\";\nimport * as log from \"../log.js\";\nimport { addLifecycleBreadcrumb, applyRunScope } from \"../sentry.js\";\nimport { formatStopped } from \"../ui-copy.js\";\nimport * as Sentry from \"@sentry/node\";\nimport { join } from \"path\";\n\nexport interface ConversationRuntimeState {\n running: boolean;\n runner: AgentRunner;\n stopRequested: boolean;\n stopMessageTs?: string;\n lastAccessedAt: number;\n startedAt?: number;\n lastActivityAt?: number;\n}\n\nexport interface RunConversationOptions {\n event: BotEvent;\n bot: Bot;\n adapters: BotAdapters;\n isSyntheticEvent?: boolean;\n}\n\ninterface ConversationOrchestratorOptions {\n workingDir: string;\n commandRegistry: CommandRegistry;\n commandServices: CommandServices;\n isShuttingDown: () => boolean;\n getState: (sessionKey: string) => ConversationRuntimeState | undefined;\n getOrCreateState: (options: {\n conversationId: string;\n platformName: string;\n sessionKey: string;\n }) => Promise<ConversationRuntimeState>;\n beforeRunTracked: (runPromise: Promise<void>) => void;\n afterRunTracked: (runPromise: Promise<void>) => void;\n onRunFinished: () => void;\n}\n\nexport class ConversationOrchestrator {\n constructor(private readonly options: ConversationOrchestratorOptions) {}\n\n async runSession({\n event,\n bot,\n adapters,\n isSyntheticEvent,\n }: RunConversationOptions): Promise<void> {\n const conversationId = event.conversationId;\n if (this.options.isShuttingDown()) {\n log.logInfo(\n `[${conversationId}] Rejected event during shutdown: ${event.text.substring(0, 50)}`,\n );\n return;\n }\n\n const sessionKey = event.sessionKey ?? `${conversationId}:${event.thread_ts ?? event.ts}`;\n const privateConversation = isPrivateConversation(event);\n const handledCommand = await this.options.commandRegistry.handle({\n bot,\n responseCtx: adapters.responseCtx,\n platform: adapters.platform.name as PlatformName,\n platformUserId: event.user,\n conversationId,\n vaultConversationId: event.vaultConversationId,\n sessionKey,\n commandText: event.text,\n privateConversation,\n services: this.options.commandServices,\n });\n if (handledCommand) return;\n\n const conversationDir = join(this.options.workingDir, conversationId);\n const waitedForParent =\n adapters.platform.name === \"slack\" && !isSyntheticEvent\n ? await waitForSlackBranchBootstrap({\n parentSessionKey: conversationId,\n sessionKey,\n hasThreadSession: () => hasMaterializedSlackBranchSession(conversationDir, sessionKey),\n isParentRunning: () => this.options.getState(conversationId)?.running === true,\n })\n : false;\n if (waitedForParent) {\n log.logInfo(\n `[${conversationId}] Delayed thread bootstrap until parent session sealed: ${sessionKey}`,\n );\n }\n\n const state = await this.options.getOrCreateState({\n conversationId,\n platformName: adapters.platform.name,\n sessionKey,\n });\n\n state.running = true;\n state.stopRequested = false;\n state.startedAt = Date.now();\n state.lastActivityAt = Date.now();\n\n log.logInfo(`[${conversationId}] Starting run: ${event.text.substring(0, 50)}`);\n\n const runPromise = (async () => {\n try {\n const result = await this.runWithInstrumentation(\n adapters,\n { conversationId, sessionKey, isSyntheticEvent, startedAt: state.startedAt! },\n async () => {\n await adapters.responseCtx.setTyping(true);\n await adapters.responseCtx.setWorking(true);\n const runnerResult = await state.runner.run(\n adapters.message,\n adapters.responseCtx,\n adapters.platform,\n );\n await adapters.responseCtx.setWorking(false);\n return runnerResult;\n },\n );\n\n if (result?.stopReason === \"aborted\" && state.stopRequested) {\n if (state.stopMessageTs) {\n await bot.updateMessage(conversationId, state.stopMessageTs, formatStopped(bot));\n state.stopMessageTs = undefined;\n } else {\n await bot.postMessage(conversationId, formatStopped(bot));\n }\n }\n } finally {\n state.running = false;\n state.lastAccessedAt = Date.now();\n this.options.onRunFinished();\n }\n })();\n\n this.options.beforeRunTracked(runPromise);\n try {\n await runPromise;\n } finally {\n this.options.afterRunTracked(runPromise);\n }\n }\n\n private async runWithInstrumentation(\n adapters: BotAdapters,\n meta: {\n conversationId: string;\n sessionKey: string;\n isSyntheticEvent?: boolean;\n startedAt: number;\n },\n body: () => Promise<{ stopReason: string; errorMessage?: string }>,\n ): Promise<{ stopReason: string; errorMessage?: string } | undefined> {\n const { conversationId, sessionKey, isSyntheticEvent, startedAt } = meta;\n const { message, platform } = adapters;\n\n Sentry.metrics.count(\"agent.run.started\", 1, {\n attributes: { channel: conversationId },\n });\n\n return Sentry.startSpan(\n { name: \"agent.run\", op: \"agent\", attributes: { conversationId, sessionKey } },\n async () =>\n Sentry.withScope(async (scope) => {\n applyRunScope(scope, {\n conversationId,\n sessionKey,\n messageId: message.id,\n platform: platform.name,\n userId: message.userId,\n userName: message.userName,\n threadTs: message.threadTs,\n isSyntheticEvent,\n });\n addLifecycleBreadcrumb(\"agent.run.started\", {\n channel_id: conversationId,\n platform: platform.name,\n has_attachments: (message.attachments?.length ?? 0) > 0,\n });\n\n try {\n const result = await body();\n const durationMs = Date.now() - startedAt;\n const completionAttrs = {\n channel: conversationId,\n platform: platform.name,\n stop_reason: result.stopReason,\n };\n Sentry.metrics.distribution(\"agent.run.duration\", durationMs, {\n unit: \"millisecond\",\n attributes: completionAttrs,\n });\n Sentry.metrics.count(\"agent.run.completed\", 1, { attributes: completionAttrs });\n addLifecycleBreadcrumb(\"agent.run.completed\", {\n channel_id: conversationId,\n platform: platform.name,\n stop_reason: result.stopReason,\n duration_ms: durationMs,\n });\n return result;\n } catch (err) {\n scope.setContext(\"agent_run_error\", {\n conversationId,\n sessionKey,\n platform: platform.name,\n messageId: message.id,\n threadTs: message.threadTs,\n });\n Sentry.captureException(err);\n Sentry.metrics.count(\"agent.run.errors\", 1, {\n attributes: { channel: conversationId, platform: platform.name },\n });\n log.logWarning(\n `[${conversationId}] Run error`,\n err instanceof Error ? err.message : String(err),\n );\n return undefined;\n }\n }),\n );\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EACpB,KAAK,2BAA2B,EAChC,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,qBAAqB,GAC3B,MAAM,sBAAsB,CAAC","sourcesContent":["export {\n createSessionRuntime,\n type CreateSessionSandboxOptions,\n type RunSessionOptions,\n type SessionRuntime,\n type SessionRuntimeOptions,\n} from \"./session-runtime.js\";\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,GAKrB,MAAM,sBAAsB,CAAC","sourcesContent":["export {\n createSessionRuntime,\n type CreateSessionSandboxOptions,\n type RunSessionOptions,\n type SessionRuntime,\n type SessionRuntimeOptions,\n} from \"./session-runtime.js\";\n"]}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Bot, BotAdapters, BotEvent, BotHandler } from "../adapter.js";
|
|
2
|
+
import { type AgentRunner } from "../agent.js";
|
|
3
|
+
import { CommandRegistry } from "../commands/index.js";
|
|
4
|
+
import type { CommandServices } from "../commands/index.js";
|
|
5
|
+
export interface RunSessionOptions {
|
|
6
|
+
event: BotEvent;
|
|
7
|
+
bot: Bot;
|
|
8
|
+
adapters: BotAdapters;
|
|
9
|
+
isSyntheticEvent?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface CreateSessionSandboxOptions {
|
|
12
|
+
conversationId: string;
|
|
13
|
+
platformName: string;
|
|
14
|
+
sessionKey: string;
|
|
15
|
+
}
|
|
16
|
+
export interface SessionRuntimeOptions extends CommandServices {
|
|
17
|
+
/** Override the default command registry (e.g., to add /help, /status). */
|
|
18
|
+
commandRegistry?: CommandRegistry;
|
|
19
|
+
}
|
|
20
|
+
export interface SessionRuntime extends BotHandler {
|
|
21
|
+
runSession(options: RunSessionOptions): Promise<void>;
|
|
22
|
+
createSessionSandbox(options: CreateSessionSandboxOptions): Promise<AgentRunner>;
|
|
23
|
+
switchConversationModel(conversationId: string, provider: string, model: string): boolean;
|
|
24
|
+
shutdown(timeoutMs?: number): Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
export declare function createSessionRuntime(options: SessionRuntimeOptions): SessionRuntime;
|
|
27
|
+
//# sourceMappingURL=session-runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-runtime.d.ts","sourceRoot":"","sources":["../../src/runtime/session-runtime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,WAAW,EAAE,QAAQ,EAAE,UAAU,EAAkB,MAAM,eAAe,CAAC;AAE5F,OAAO,EAAE,KAAK,WAAW,EAAgB,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAgC,MAAM,sBAAsB,CAAC;AACrF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAqB5D,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,QAAQ,CAAC;IAChB,GAAG,EAAE,GAAG,CAAC;IACT,QAAQ,EAAE,WAAW,CAAC;IACtB,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,2BAA2B;IAC1C,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,qBAAsB,SAAQ,eAAe;IAC5D,2EAA2E;IAC3E,eAAe,CAAC,EAAE,eAAe,CAAC;CACnC;AAED,MAAM,WAAW,cAAe,SAAQ,UAAU;IAChD,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,oBAAoB,CAAC,OAAO,EAAE,2BAA2B,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IACjF,uBAAuB,CAAC,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IAC1F,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7C;AAiBD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,qBAAqB,GAAG,cAAc,CAEnF","sourcesContent":["import type { Bot, BotAdapters, BotEvent, BotHandler, RunningSession } from \"../adapter.js\";\nimport { resolveSlackSessionScope } from \"../adapters/slack/branch-manager.js\";\nimport { type AgentRunner, createRunner } from \"../agent.js\";\nimport { CommandRegistry, createDefaultCommandRegistry } from \"../commands/index.js\";\nimport type { CommandServices } from \"../commands/index.js\";\nimport * as log from \"../log.js\";\nimport {\n createManagedSessionFile,\n createManagedSessionFileAtPath,\n getChannelSessionDir,\n getThreadSessionFile,\n resolveGenericSessionScope,\n type ResolvedSessionScope,\n} from \"../session-store.js\";\nimport { formatNothingRunning, formatStopping } from \"../ui-copy.js\";\nimport {\n ConversationOrchestrator,\n type ConversationRuntimeState,\n} from \"./conversation-orchestrator.js\";\nimport * as Sentry from \"@sentry/node\";\nimport { join } from \"path\";\nimport { getUnresolvedSandboxPathContext } from \"../agent.js\";\n\ntype ConversationState = ConversationRuntimeState;\n\nexport interface RunSessionOptions {\n event: BotEvent;\n bot: Bot;\n adapters: BotAdapters;\n isSyntheticEvent?: boolean;\n}\n\nexport interface CreateSessionSandboxOptions {\n conversationId: string;\n platformName: string;\n sessionKey: string;\n}\n\nexport interface SessionRuntimeOptions extends CommandServices {\n /** Override the default command registry (e.g., to add /help, /status). */\n commandRegistry?: CommandRegistry;\n}\n\nexport interface SessionRuntime extends BotHandler {\n runSession(options: RunSessionOptions): Promise<void>;\n createSessionSandbox(options: CreateSessionSandboxOptions): Promise<AgentRunner>;\n switchConversationModel(conversationId: string, provider: string, model: string): boolean;\n shutdown(timeoutMs?: number): Promise<void>;\n}\n\nconst MAX_SESSIONS = 500;\nconst IDLE_TIMEOUT_MS = 3_600_000;\n\nfunction runtimeCwdForSandbox(\n sandbox: SessionRuntimeOptions[\"sandbox\"],\n hostWorkspaceRoot: string,\n conversationId: string,\n): string {\n const runtimeWorkspaceRoot = getUnresolvedSandboxPathContext(\n sandbox,\n hostWorkspaceRoot,\n ).runtimeWorkspaceRoot;\n return `${runtimeWorkspaceRoot.replace(/\\/+$/, \"\")}/${conversationId}`;\n}\n\nexport function createSessionRuntime(options: SessionRuntimeOptions): SessionRuntime {\n return new MamaSessionRuntime(options);\n}\n\nclass MamaSessionRuntime implements SessionRuntime {\n private readonly conversationStates = new Map<string, ConversationState>();\n private readonly sessionQueues = new Map<string, Promise<void>>();\n private readonly inFlightRuns = new Set<Promise<void>>();\n private readonly commandRegistry: CommandRegistry;\n private readonly orchestrator: ConversationOrchestrator;\n private isShuttingDown = false;\n\n constructor(private readonly options: SessionRuntimeOptions) {\n this.options.runtime = this;\n this.commandRegistry = options.commandRegistry ?? createDefaultCommandRegistry();\n this.orchestrator = new ConversationOrchestrator({\n workingDir: options.workingDir,\n commandRegistry: this.commandRegistry,\n commandServices: this.options,\n isShuttingDown: () => this.isShuttingDown,\n getState: (sessionKey) => this.conversationStates.get(sessionKey),\n getOrCreateState: (createOptions) => this.getOrCreateState(createOptions),\n beforeRunTracked: (runPromise) => {\n this.inFlightRuns.add(runPromise);\n Sentry.metrics.gauge(\"agent.sessions.active\", this.inFlightRuns.size);\n },\n afterRunTracked: (runPromise) => {\n this.inFlightRuns.delete(runPromise);\n },\n onRunFinished: () => {\n Sentry.metrics.gauge(\"agent.sessions.active\", this.inFlightRuns.size - 1);\n this.evictIdleSessions();\n },\n });\n }\n\n isRunning(sessionKey: string): boolean {\n const state = this.conversationStates.get(sessionKey);\n return !!state?.running;\n }\n\n getRunningSessions(): RunningSession[] {\n const sessions: RunningSession[] = [];\n for (const [sessionKey, state] of this.conversationStates) {\n if (state.running && state.startedAt) {\n const currentStep = state.runner.getCurrentStep();\n sessions.push({\n sessionKey,\n startedAt: state.startedAt,\n lastActivityAt: state.lastActivityAt,\n currentTool: currentStep?.label || currentStep?.toolName,\n });\n }\n }\n return sessions;\n }\n\n async handleStop(sessionKey: string, conversationId: string, bot: Bot): Promise<void> {\n const state = this.conversationStates.get(sessionKey);\n if (state?.running) {\n state.stopRequested = true;\n state.runner.abort();\n const ts = await bot.postMessage(conversationId, formatStopping(bot));\n state.stopMessageTs = ts;\n } else {\n await bot.postMessage(conversationId, formatNothingRunning(bot));\n }\n }\n\n forceStop(sessionKey: string): void {\n const state = this.conversationStates.get(sessionKey);\n if (state?.running) {\n log.logInfo(`[Force Stop] Force stopping session: ${sessionKey}`);\n state.stopRequested = true;\n state.runner.abort();\n state.running = false;\n }\n }\n\n async handleNewCommand(sessionKey: string, conversationId: string, bot: Bot): Promise<void> {\n const state = this.conversationStates.get(sessionKey);\n if (state?.running) {\n state.stopRequested = true;\n state.runner.abort();\n }\n\n const conversationDir = join(this.options.workingDir, conversationId);\n const runtimeCwd = runtimeCwdForSandbox(\n this.options.sandbox,\n this.options.workingDir,\n conversationId,\n );\n if (sessionKey.includes(\":\")) {\n createManagedSessionFileAtPath(getThreadSessionFile(conversationDir, sessionKey), runtimeCwd);\n } else {\n createManagedSessionFile(getChannelSessionDir(conversationDir), runtimeCwd);\n }\n\n this.conversationStates.delete(sessionKey);\n\n log.logInfo(`[${conversationId}] Session reset: ${sessionKey}`);\n await bot.postMessage(conversationId, \"Conversation reset. Send a new message to start fresh.\");\n }\n\n async handleEvent(\n event: BotEvent,\n bot: Bot,\n adapters: BotAdapters,\n isSyntheticEvent?: boolean,\n ): Promise<void> {\n const sessionKey = event.sessionKey ?? `${event.conversationId}:${event.thread_ts ?? event.ts}`;\n const previous = this.sessionQueues.get(sessionKey) ?? Promise.resolve();\n const next = previous\n .catch(() => {})\n .then(() => this.runSession({ event, bot, adapters, isSyntheticEvent }));\n this.sessionQueues.set(sessionKey, next);\n try {\n await next;\n } finally {\n if (this.sessionQueues.get(sessionKey) === next) {\n this.sessionQueues.delete(sessionKey);\n }\n }\n }\n\n async runSession({ event, bot, adapters, isSyntheticEvent }: RunSessionOptions): Promise<void> {\n await this.orchestrator.runSession({ event, bot, adapters, isSyntheticEvent });\n }\n\n async createSessionSandbox(options: CreateSessionSandboxOptions): Promise<AgentRunner> {\n const state = await this.getOrCreateState(options);\n return state.runner;\n }\n\n switchConversationModel(conversationId: string, _provider: string, _model: string): boolean {\n for (const [sessionKey, state] of this.conversationStates) {\n if (this.isConversationSession(sessionKey, conversationId) && state.running) {\n return false;\n }\n }\n\n for (const sessionKey of Array.from(this.conversationStates.keys())) {\n if (this.isConversationSession(sessionKey, conversationId)) {\n this.conversationStates.delete(sessionKey);\n }\n }\n log.logInfo(`[${conversationId}] Model switched; cleared cached session runners`);\n return true;\n }\n\n private isConversationSession(sessionKey: string, conversationId: string): boolean {\n return sessionKey === conversationId || sessionKey.startsWith(`${conversationId}:`);\n }\n\n private async getOrCreateState({\n conversationId,\n platformName,\n sessionKey,\n }: CreateSessionSandboxOptions): Promise<ConversationState> {\n const existing = this.conversationStates.get(sessionKey);\n if (existing) {\n existing.lastAccessedAt = Date.now();\n return existing;\n }\n\n const conversationDir = join(this.options.workingDir, conversationId);\n const runtimeCwd = runtimeCwdForSandbox(\n this.options.sandbox,\n this.options.workingDir,\n conversationId,\n );\n const sessionScope = await this.resolveSessionScope(\n platformName,\n conversationDir,\n sessionKey,\n runtimeCwd,\n );\n const state: ConversationState = {\n running: false,\n runner: await createRunner(\n this.options.sandbox,\n sessionKey,\n conversationId,\n conversationDir,\n this.options.workingDir,\n sessionScope,\n this.options.vaultManager,\n this.options.provisioner,\n ),\n stopRequested: false,\n lastAccessedAt: Date.now(),\n };\n this.conversationStates.set(sessionKey, state);\n return state;\n }\n\n async shutdown(timeoutMs = 30_000): Promise<void> {\n if (this.isShuttingDown) return;\n this.isShuttingDown = true;\n log.logInfo(\"Shutting down gracefully...\");\n\n const timeout = Date.now() + timeoutMs;\n while (this.inFlightRuns.size > 0 && Date.now() < timeout) {\n await new Promise((resolve) => setTimeout(resolve, 500));\n }\n\n if (this.inFlightRuns.size > 0) {\n log.logWarning(`Forcing exit with ${this.inFlightRuns.size} runs still in progress`);\n }\n }\n\n private async resolveSessionScope(\n platformName: string,\n conversationDir: string,\n sessionKey: string,\n cwd: string,\n ): Promise<ResolvedSessionScope> {\n if (sessionKey.includes(\":event:\")) {\n const sessionDir = getChannelSessionDir(conversationDir);\n const contextFile = createManagedSessionFileAtPath(\n getThreadSessionFile(conversationDir, sessionKey),\n cwd,\n );\n return { sessionDir, contextFile, threadRootMessage: null };\n }\n\n if (platformName === \"slack\") {\n return resolveSlackSessionScope({ conversationDir, sessionKey, cwd });\n }\n return resolveGenericSessionScope({ conversationDir, sessionKey, cwd });\n }\n\n private evictIdleSessions(): void {\n const now = Date.now();\n\n for (const [key, state] of this.conversationStates) {\n if (!state.running && now - state.lastAccessedAt > IDLE_TIMEOUT_MS) {\n this.conversationStates.delete(key);\n }\n }\n\n if (this.conversationStates.size > MAX_SESSIONS) {\n const idleSessions: Array<{ key: string; lastAccessedAt: number }> = [];\n for (const [key, state] of this.conversationStates) {\n if (!state.running) {\n idleSessions.push({ key, lastAccessedAt: state.lastAccessedAt });\n }\n }\n\n idleSessions.sort((a, b) => a.lastAccessedAt - b.lastAccessedAt);\n\n const toEvict = this.conversationStates.size - MAX_SESSIONS;\n for (let i = 0; i < toEvict && i < idleSessions.length; i++) {\n this.conversationStates.delete(idleSessions[i].key);\n }\n }\n }\n}\n"]}
|