@geminixiang/mama 0.2.0-beta.4 → 0.2.0-beta.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/README.md +100 -421
  2. package/dist/adapter.d.ts +9 -0
  3. package/dist/adapter.d.ts.map +1 -1
  4. package/dist/adapter.js.map +1 -1
  5. package/dist/adapters/discord/bot.d.ts +1 -0
  6. package/dist/adapters/discord/bot.d.ts.map +1 -1
  7. package/dist/adapters/discord/bot.js +4 -1
  8. package/dist/adapters/discord/bot.js.map +1 -1
  9. package/dist/adapters/discord/context.d.ts.map +1 -1
  10. package/dist/adapters/discord/context.js +9 -2
  11. package/dist/adapters/discord/context.js.map +1 -1
  12. package/dist/adapters/slack/bot.d.ts +1 -0
  13. package/dist/adapters/slack/bot.d.ts.map +1 -1
  14. package/dist/adapters/slack/bot.js +9 -3
  15. package/dist/adapters/slack/bot.js.map +1 -1
  16. package/dist/adapters/slack/context.d.ts.map +1 -1
  17. package/dist/adapters/slack/context.js +13 -3
  18. package/dist/adapters/slack/context.js.map +1 -1
  19. package/dist/adapters/telegram/context.d.ts.map +1 -1
  20. package/dist/adapters/telegram/context.js +9 -2
  21. package/dist/adapters/telegram/context.js.map +1 -1
  22. package/dist/agent.d.ts.map +1 -1
  23. package/dist/agent.js +13 -5
  24. package/dist/agent.js.map +1 -1
  25. package/dist/bindings.d.ts +2 -1
  26. package/dist/bindings.d.ts.map +1 -1
  27. package/dist/bindings.js.map +1 -1
  28. package/dist/commands/index.d.ts +5 -0
  29. package/dist/commands/index.d.ts.map +1 -0
  30. package/dist/commands/index.js +8 -0
  31. package/dist/commands/index.js.map +1 -0
  32. package/dist/commands/login.d.ts +5 -0
  33. package/dist/commands/login.d.ts.map +1 -0
  34. package/dist/commands/login.js +37 -0
  35. package/dist/commands/login.js.map +1 -0
  36. package/dist/commands/registry.d.ts +7 -0
  37. package/dist/commands/registry.d.ts.map +1 -0
  38. package/dist/commands/registry.js +14 -0
  39. package/dist/commands/registry.js.map +1 -0
  40. package/dist/commands/session-view.d.ts +5 -0
  41. package/dist/commands/session-view.d.ts.map +1 -0
  42. package/dist/commands/session-view.js +38 -0
  43. package/dist/commands/session-view.js.map +1 -0
  44. package/dist/commands/types.d.ts +41 -0
  45. package/dist/commands/types.d.ts.map +1 -0
  46. package/dist/commands/types.js +2 -0
  47. package/dist/commands/types.js.map +1 -0
  48. package/dist/commands/utils.d.ts +5 -0
  49. package/dist/commands/utils.d.ts.map +1 -0
  50. package/dist/commands/utils.js +9 -0
  51. package/dist/commands/utils.js.map +1 -0
  52. package/dist/config.d.ts +4 -4
  53. package/dist/config.d.ts.map +1 -1
  54. package/dist/config.js +34 -40
  55. package/dist/config.js.map +1 -1
  56. package/dist/context.d.ts.map +1 -1
  57. package/dist/context.js +74 -68
  58. package/dist/context.js.map +1 -1
  59. package/dist/execution-resolver.d.ts +6 -3
  60. package/dist/execution-resolver.d.ts.map +1 -1
  61. package/dist/execution-resolver.js +47 -14
  62. package/dist/execution-resolver.js.map +1 -1
  63. package/dist/index.d.ts +7 -0
  64. package/dist/index.d.ts.map +1 -0
  65. package/dist/index.js +4 -0
  66. package/dist/index.js.map +1 -0
  67. package/dist/instrument.d.ts.map +1 -1
  68. package/dist/instrument.js +2 -3
  69. package/dist/instrument.js.map +1 -1
  70. package/dist/login/index.d.ts.map +1 -1
  71. package/dist/login/index.js +19 -8
  72. package/dist/login/index.js.map +1 -1
  73. package/dist/login/portal.d.ts.map +1 -1
  74. package/dist/login/portal.js +7 -7
  75. package/dist/login/portal.js.map +1 -1
  76. package/dist/login/session.d.ts +3 -2
  77. package/dist/login/session.d.ts.map +1 -1
  78. package/dist/login/session.js.map +1 -1
  79. package/dist/main.d.ts.map +1 -1
  80. package/dist/main.js +62 -386
  81. package/dist/main.js.map +1 -1
  82. package/dist/provisioner.d.ts +11 -9
  83. package/dist/provisioner.d.ts.map +1 -1
  84. package/dist/provisioner.js +125 -87
  85. package/dist/provisioner.js.map +1 -1
  86. package/dist/runtime/index.d.ts +2 -0
  87. package/dist/runtime/index.d.ts.map +1 -0
  88. package/dist/runtime/index.js +2 -0
  89. package/dist/runtime/index.js.map +1 -0
  90. package/dist/runtime/session-runtime.d.ts +26 -0
  91. package/dist/runtime/session-runtime.d.ts.map +1 -0
  92. package/dist/runtime/session-runtime.js +285 -0
  93. package/dist/runtime/session-runtime.js.map +1 -0
  94. package/dist/sandbox/cloudflare.d.ts +14 -0
  95. package/dist/sandbox/cloudflare.d.ts.map +1 -0
  96. package/dist/sandbox/cloudflare.js +131 -0
  97. package/dist/sandbox/cloudflare.js.map +1 -0
  98. package/dist/sandbox/index.d.ts +6 -4
  99. package/dist/sandbox/index.d.ts.map +1 -1
  100. package/dist/sandbox/index.js +6 -3
  101. package/dist/sandbox/index.js.map +1 -1
  102. package/dist/sandbox/types.d.ts +5 -1
  103. package/dist/sandbox/types.d.ts.map +1 -1
  104. package/dist/sandbox/types.js.map +1 -1
  105. package/dist/session-view/portal.d.ts.map +1 -1
  106. package/dist/session-view/portal.js +10 -1
  107. package/dist/session-view/portal.js.map +1 -1
  108. package/dist/session-view/service.d.ts.map +1 -1
  109. package/dist/session-view/service.js +36 -26
  110. package/dist/session-view/service.js.map +1 -1
  111. package/dist/session-view/store.d.ts +3 -2
  112. package/dist/session-view/store.d.ts.map +1 -1
  113. package/dist/session-view/store.js.map +1 -1
  114. package/dist/vault-routing.d.ts +3 -5
  115. package/dist/vault-routing.d.ts.map +1 -1
  116. package/dist/vault-routing.js +8 -20
  117. package/dist/vault-routing.js.map +1 -1
  118. package/dist/vault.d.ts +7 -5
  119. package/dist/vault.d.ts.map +1 -1
  120. package/dist/vault.js +101 -50
  121. package/dist/vault.js.map +1 -1
  122. package/package.json +1 -2
@@ -2,13 +2,26 @@ import { execFile } from "child_process";
2
2
  import { promisify } from "util";
3
3
  import * as log from "./log.js";
4
4
  const execFileAsync = promisify(execFile);
5
+ function isDockerNotFoundError(err) {
6
+ if (!err || typeof err !== "object")
7
+ return false;
8
+ const stderr = err.stderr;
9
+ const message = err.message;
10
+ const haystack = `${typeof stderr === "string" ? stderr : ""}\n${typeof message === "string" ? message : ""}`.toLowerCase();
11
+ return (haystack.includes("no such network") ||
12
+ haystack.includes("no such container") ||
13
+ haystack.includes("no such object") ||
14
+ haystack.includes("network not found") ||
15
+ /network [^\n]+ not found/.test(haystack) ||
16
+ /error: no such [^\n]+/.test(haystack));
17
+ }
5
18
  export class DockerContainerManager {
6
19
  static { this.MANAGED_LABEL = "mama.managed=true"; }
7
20
  static { this.IMAGE_MODE_LABEL = "mama.sandbox=image"; }
8
21
  static { this.VAULT_ID_LABEL_KEY = "mama.vault-id"; }
9
- constructor(image, workspaceDir, options = {}) {
22
+ static { this.CONVERSATION_ID_LABEL_KEY = "mama.conversation-id"; }
23
+ constructor(image, options = {}) {
10
24
  this.image = image;
11
- this.workspaceDir = workspaceDir;
12
25
  this.state = new Map();
13
26
  this.inflight = new Map();
14
27
  if (typeof options === "function") {
@@ -26,34 +39,32 @@ export class DockerContainerManager {
26
39
  .replace(/^-+|-+$/g, "");
27
40
  return sanitized || "unknown";
28
41
  }
29
- static vaultId(platform, platformUserId) {
30
- return `${DockerContainerManager.sanitizeSegment(platform)}-${DockerContainerManager.sanitizeSegment(platformUserId)}`;
31
- }
32
- static containerName(vaultId) {
33
- return `mama-sandbox-${vaultId}`;
42
+ static containerName(containerKey) {
43
+ return `mama-sandbox-${containerKey}`;
34
44
  }
35
- static networkName(vaultId) {
36
- return `mama-sandbox-net-${vaultId}`;
45
+ static networkName(containerKey) {
46
+ return `mama-sandbox-net-${containerKey}`;
37
47
  }
38
- async provision(vaultId, options = {}) {
39
- const existing = this.inflight.get(vaultId);
48
+ async provision(containerKey, options = {}) {
49
+ const existing = this.inflight.get(containerKey);
40
50
  if (existing)
41
51
  return existing;
42
- const pending = this.provisionInner(vaultId, options).finally(() => {
43
- this.inflight.delete(vaultId);
52
+ const pending = this.provisionInner(containerKey, options).finally(() => {
53
+ this.inflight.delete(containerKey);
44
54
  });
45
- this.inflight.set(vaultId, pending);
55
+ this.inflight.set(containerKey, pending);
46
56
  return pending;
47
57
  }
48
- async provisionInner(vaultId, options) {
49
- const containerName = options.containerName ?? DockerContainerManager.containerName(vaultId);
58
+ async provisionInner(containerKey, options) {
59
+ const containerName = options.containerName ?? DockerContainerManager.containerName(containerKey);
50
60
  const mounts = options.mounts ?? [];
51
61
  const status = await this.inspectStatus(containerName);
52
62
  try {
53
- if (status !== "missing" && (await this.hasRuntimeDrift(vaultId, containerName, mounts))) {
63
+ if (status !== "missing" &&
64
+ (await this.hasRuntimeDrift(containerKey, containerName, mounts))) {
54
65
  log.logInfo(`Container ${containerName} configuration changed; recreating container`);
55
66
  await this.execFileImpl("docker", ["rm", "-f", containerName]);
56
- await this.runContainer(vaultId, containerName, mounts);
67
+ await this.runContainer(containerKey, containerName, mounts, options);
57
68
  log.logInfo(`Container ${containerName} recreated`);
58
69
  }
59
70
  else if (status === "running") {
@@ -64,39 +75,33 @@ export class DockerContainerManager {
64
75
  log.logInfo(`Container ${containerName} started`);
65
76
  }
66
77
  else {
67
- await this.runContainer(vaultId, containerName, mounts);
78
+ await this.runContainer(containerKey, containerName, mounts, options);
68
79
  log.logInfo(`Container ${containerName} created`);
69
80
  }
70
81
  }
71
82
  catch (err) {
72
- this.state.delete(vaultId);
83
+ this.state.delete(containerKey);
73
84
  throw err;
74
85
  }
75
- this.setState(vaultId, "running", containerName);
86
+ this.setState(containerKey, "running", containerName);
76
87
  await this.applyResourceLimits(containerName);
77
88
  return containerName;
78
89
  }
79
- async stop(vaultId) {
80
- const containerName = this.getContainerName(vaultId);
90
+ async stop(containerKey) {
91
+ const containerName = this.getContainerName(containerKey);
81
92
  try {
82
93
  await this.execFileImpl("docker", ["stop", containerName]);
83
- this.setState(vaultId, "stopped", containerName);
94
+ this.setState(containerKey, "stopped", containerName);
84
95
  log.logInfo(`Container ${containerName} stopped (idle)`);
85
96
  }
86
97
  catch (err) {
87
98
  log.logWarning(`Failed to stop container ${containerName}`, err instanceof Error ? err.message : String(err));
88
99
  }
89
100
  }
90
- async remove(vaultId) {
91
- const containerName = this.getContainerName(vaultId);
92
- const networkName = DockerContainerManager.networkName(vaultId);
93
- try {
94
- await this.execFileImpl("docker", ["rm", "-f", containerName]);
95
- log.logInfo(`Container ${containerName} removed`);
96
- }
97
- catch (err) {
98
- log.logWarning(`Failed to remove container ${containerName}`, err instanceof Error ? err.message : String(err));
99
- }
101
+ async remove(containerKey) {
102
+ const containerName = this.getContainerName(containerKey);
103
+ const networkName = DockerContainerManager.networkName(containerKey);
104
+ await this.forceRemoveContainer(containerName, `Container ${containerName} removed`, `Failed to remove container ${containerName}`);
100
105
  try {
101
106
  await this.execFileImpl("docker", ["network", "rm", networkName]);
102
107
  log.logInfo(`Network ${networkName} removed`);
@@ -104,17 +109,17 @@ export class DockerContainerManager {
104
109
  catch (err) {
105
110
  log.logWarning(`Failed to remove network ${networkName}`, err instanceof Error ? err.message : String(err));
106
111
  }
107
- this.state.delete(vaultId);
112
+ this.state.delete(containerKey);
108
113
  }
109
114
  async stopIdle(maxIdleMs) {
110
115
  const now = Date.now();
111
116
  const toStop = [];
112
- for (const [vaultId, containerState] of this.state) {
117
+ for (const [containerKey, containerState] of this.state) {
113
118
  if (containerState.status === "running" && now - containerState.lastUsed > maxIdleMs) {
114
- toStop.push(vaultId);
119
+ toStop.push(containerKey);
115
120
  }
116
121
  }
117
- await Promise.all(toStop.map((vaultId) => this.stop(vaultId)));
122
+ await Promise.all(toStop.map((containerKey) => this.stop(containerKey)));
118
123
  }
119
124
  async reconcile() {
120
125
  const discovered = new Set();
@@ -125,28 +130,38 @@ export class DockerContainerManager {
125
130
  for (const name of legacyNames)
126
131
  discovered.add(name);
127
132
  this.state.clear();
128
- for (const containerName of discovered) {
129
- const details = await this.inspectContainerDetails(containerName);
133
+ const inspected = await Promise.all(Array.from(discovered).map(async (containerName) => ({
134
+ containerName,
135
+ details: await this.inspectContainerDetails(containerName),
136
+ })));
137
+ const legacyRemovals = [];
138
+ for (const { containerName, details } of inspected) {
130
139
  if (!details)
131
140
  continue;
132
- const vaultId = details.vaultId || this.vaultIdFromContainerName(containerName);
133
- if (!vaultId) {
134
- log.logWarning(`Skipping unmanaged-style container without vault id`, containerName);
141
+ if (!details.conversationId) {
142
+ legacyRemovals.push(this.removeLegacyContainer(containerName));
143
+ continue;
144
+ }
145
+ const containerKey = this.containerKeyFromContainerName(containerName);
146
+ if (!containerKey) {
147
+ log.logWarning(`Skipping unmanaged-style container without container key`, containerName);
135
148
  continue;
136
149
  }
137
150
  const status = details.running ? "running" : "stopped";
138
151
  const lastUsed = details.startedAtMs ?? Date.now();
139
- this.state.set(vaultId, { status, lastUsed, containerName });
152
+ this.state.set(containerKey, { status, lastUsed, containerName });
140
153
  }
154
+ await Promise.all(legacyRemovals);
141
155
  const running = Array.from(this.state.values()).filter((s) => s.status === "running").length;
142
156
  const stopped = this.state.size - running;
143
157
  log.logInfo(`Reconciled ${this.state.size} managed containers (running=${running}, stopped=${stopped})`);
144
158
  }
145
- setState(vaultId, status, containerName) {
146
- this.state.set(vaultId, { status, lastUsed: Date.now(), containerName });
159
+ setState(containerKey, status, containerName) {
160
+ this.state.set(containerKey, { status, lastUsed: Date.now(), containerName });
147
161
  }
148
- getContainerName(vaultId) {
149
- return this.state.get(vaultId)?.containerName ?? DockerContainerManager.containerName(vaultId);
162
+ getContainerName(containerKey) {
163
+ return (this.state.get(containerKey)?.containerName ??
164
+ DockerContainerManager.containerName(containerKey));
150
165
  }
151
166
  mountArgs(mounts) {
152
167
  return mounts.flatMap((mount) => ["-v", this.toBindSpec(mount)]);
@@ -154,9 +169,20 @@ export class DockerContainerManager {
154
169
  toBindSpec(mount) {
155
170
  return `${mount.source}:${mount.target}`;
156
171
  }
157
- async runContainer(vaultId, containerName, mounts) {
158
- const networkName = await this.ensureNetwork(vaultId);
172
+ async runContainer(containerKey, containerName, mounts, options) {
173
+ const networkName = await this.ensureNetwork(containerKey);
159
174
  log.logInfo(`Creating container ${containerName} from image ${this.image}`);
175
+ const labels = [
176
+ "--label",
177
+ DockerContainerManager.MANAGED_LABEL,
178
+ "--label",
179
+ DockerContainerManager.IMAGE_MODE_LABEL,
180
+ "--label",
181
+ `${DockerContainerManager.VAULT_ID_LABEL_KEY}=${containerKey}`,
182
+ ];
183
+ if (options.conversationId) {
184
+ labels.push("--label", `${DockerContainerManager.CONVERSATION_ID_LABEL_KEY}=${options.conversationId}`);
185
+ }
160
186
  await this.execFileImpl("docker", [
161
187
  "run",
162
188
  "-d",
@@ -164,15 +190,8 @@ export class DockerContainerManager {
164
190
  containerName,
165
191
  "--network",
166
192
  networkName,
167
- "--label",
168
- DockerContainerManager.MANAGED_LABEL,
169
- "--label",
170
- DockerContainerManager.IMAGE_MODE_LABEL,
171
- "--label",
172
- `${DockerContainerManager.VAULT_ID_LABEL_KEY}=${vaultId}`,
193
+ ...labels,
173
194
  ...this.resourceLimitArgs(),
174
- "-v",
175
- `${this.workspaceDir}:/workspace`,
176
195
  ...this.mountArgs(mounts),
177
196
  this.image,
178
197
  "sleep",
@@ -198,11 +217,11 @@ export class DockerContainerManager {
198
217
  log.logWarning(`Failed to apply resource limits to container ${containerName}`, err instanceof Error ? err.message : String(err));
199
218
  }
200
219
  }
201
- async hasRuntimeDrift(vaultId, containerName, mounts) {
220
+ async hasRuntimeDrift(containerKey, containerName, mounts) {
202
221
  if (await this.hasBindMountDrift(containerName, mounts)) {
203
222
  return true;
204
223
  }
205
- return this.hasNetworkModeDrift(vaultId, containerName);
224
+ return this.hasNetworkModeDrift(containerKey, containerName);
206
225
  }
207
226
  async hasBindMountDrift(containerName, mounts) {
208
227
  const expected = this.expectedBinds(mounts);
@@ -210,7 +229,8 @@ export class DockerContainerManager {
210
229
  return !this.sameBinds(expected, actual);
211
230
  }
212
231
  expectedBinds(mounts) {
213
- return [`${this.workspaceDir}:/workspace`, ...mounts.map((mount) => this.toBindSpec(mount))]
232
+ return mounts
233
+ .map((mount) => this.toBindSpec(mount))
214
234
  .slice()
215
235
  .sort();
216
236
  }
@@ -237,8 +257,8 @@ export class DockerContainerManager {
237
257
  }
238
258
  return [...parsed].sort();
239
259
  }
240
- async hasNetworkModeDrift(vaultId, containerName) {
241
- const expected = DockerContainerManager.networkName(vaultId);
260
+ async hasNetworkModeDrift(containerKey, containerName) {
261
+ const expected = DockerContainerManager.networkName(containerKey);
242
262
  const { stdout } = await this.execFileImpl("docker", [
243
263
  "inspect",
244
264
  "-f",
@@ -247,26 +267,29 @@ export class DockerContainerManager {
247
267
  ]);
248
268
  return stdout.trim() !== expected;
249
269
  }
250
- async ensureNetwork(vaultId) {
251
- const networkName = DockerContainerManager.networkName(vaultId);
270
+ async ensureNetwork(containerKey) {
271
+ const networkName = DockerContainerManager.networkName(containerKey);
252
272
  try {
253
273
  await this.execFileImpl("docker", ["network", "inspect", networkName]);
274
+ return networkName;
254
275
  }
255
- catch {
256
- await this.execFileImpl("docker", [
257
- "network",
258
- "create",
259
- "--driver",
260
- "bridge",
261
- "--label",
262
- DockerContainerManager.MANAGED_LABEL,
263
- "--label",
264
- DockerContainerManager.IMAGE_MODE_LABEL,
265
- "--label",
266
- `${DockerContainerManager.VAULT_ID_LABEL_KEY}=${vaultId}`,
267
- networkName,
268
- ]);
276
+ catch (err) {
277
+ if (!isDockerNotFoundError(err))
278
+ throw err;
269
279
  }
280
+ await this.execFileImpl("docker", [
281
+ "network",
282
+ "create",
283
+ "--driver",
284
+ "bridge",
285
+ "--label",
286
+ DockerContainerManager.MANAGED_LABEL,
287
+ "--label",
288
+ DockerContainerManager.IMAGE_MODE_LABEL,
289
+ "--label",
290
+ `${DockerContainerManager.VAULT_ID_LABEL_KEY}=${containerKey}`,
291
+ networkName,
292
+ ]);
270
293
  return networkName;
271
294
  }
272
295
  async inspectStatus(containerName) {
@@ -279,8 +302,10 @@ export class DockerContainerManager {
279
302
  ]);
280
303
  return stdout.trim() === "true" ? "running" : "stopped";
281
304
  }
282
- catch {
283
- return "missing";
305
+ catch (err) {
306
+ if (isDockerNotFoundError(err))
307
+ return "missing";
308
+ throw err;
284
309
  }
285
310
  }
286
311
  async listContainerNamesByLabel() {
@@ -330,14 +355,15 @@ export class DockerContainerManager {
330
355
  const { stdout } = await this.execFileImpl("docker", [
331
356
  "inspect",
332
357
  "-f",
333
- `{{.State.Running}}\t{{.State.StartedAt}}\t{{index .Config.Labels "${DockerContainerManager.VAULT_ID_LABEL_KEY}"}}`,
358
+ `{{.State.Running}}\t{{.State.StartedAt}}\t{{index .Config.Labels "${DockerContainerManager.VAULT_ID_LABEL_KEY}"}}\t{{index .Config.Labels "${DockerContainerManager.CONVERSATION_ID_LABEL_KEY}"}}`,
334
359
  containerName,
335
360
  ]);
336
- const [runningRaw, startedAtRaw, vaultIdRaw] = stdout.trim().split("\t");
361
+ const [runningRaw, startedAtRaw, vaultIdRaw, conversationIdRaw] = stdout.trim().split("\t");
337
362
  const running = runningRaw === "true";
338
363
  const startedAtMs = this.parseDockerTimestamp(startedAtRaw);
339
364
  const vaultId = this.normalizeDockerValue(vaultIdRaw);
340
- return { running, startedAtMs, vaultId };
365
+ const conversationId = this.normalizeDockerValue(conversationIdRaw);
366
+ return { running, startedAtMs, vaultId, conversationId };
341
367
  }
342
368
  catch (err) {
343
369
  log.logWarning(`Failed to inspect container ${containerName} during reconcile`, err instanceof Error ? err.message : String(err));
@@ -357,12 +383,24 @@ export class DockerContainerManager {
357
383
  const parsed = Date.parse(normalized);
358
384
  return Number.isNaN(parsed) ? undefined : parsed;
359
385
  }
360
- vaultIdFromContainerName(containerName) {
386
+ containerKeyFromContainerName(containerName) {
361
387
  const prefix = DockerContainerManager.containerName("");
362
388
  if (!containerName.startsWith(prefix))
363
389
  return undefined;
364
- const vaultId = containerName.slice(prefix.length);
365
- return vaultId.length > 0 ? vaultId : undefined;
390
+ const containerKey = containerName.slice(prefix.length);
391
+ return containerKey.length > 0 ? containerKey : undefined;
392
+ }
393
+ async forceRemoveContainer(containerName, successLog, failureLog) {
394
+ try {
395
+ await this.execFileImpl("docker", ["rm", "-f", containerName]);
396
+ log.logInfo(successLog);
397
+ }
398
+ catch (err) {
399
+ log.logWarning(failureLog, err instanceof Error ? err.message : String(err));
400
+ }
401
+ }
402
+ async removeLegacyContainer(containerName) {
403
+ await this.forceRemoveContainer(containerName, `Removed legacy mama container ${containerName} (pre-channel-isolation scheme)`, `Failed to remove legacy mama container ${containerName}`);
366
404
  }
367
405
  }
368
406
  /** @deprecated Use DockerContainerManager */
@@ -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 ResourceLimits {\n cpus?: string;\n memory?: string;\n}\n\nexport interface ProvisionOptions {\n containerName?: string;\n mounts?: ContainerMount[];\n}\n\nexport interface DockerContainerManagerOptions {\n limits?: 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\n private readonly limits?: ResourceLimits;\n private readonly execFileImpl: ExecFileAsync;\n\n constructor(\n private readonly image: string,\n private readonly workspaceDir: string,\n options: DockerContainerManagerOptions | ExecFileAsync = {},\n ) {\n if (typeof options === \"function\") {\n this.execFileImpl = options;\n } else {\n this.limits = options.limits;\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 vaultId(platform: string, platformUserId: string): string {\n return `${DockerContainerManager.sanitizeSegment(platform)}-${DockerContainerManager.sanitizeSegment(platformUserId)}`;\n }\n\n static containerName(vaultId: string): string {\n return `mama-sandbox-${vaultId}`;\n }\n\n static networkName(vaultId: string): string {\n return `mama-sandbox-net-${vaultId}`;\n }\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.hasRuntimeDrift(vaultId, containerName, mounts))) {\n log.logInfo(`Container ${containerName} configuration 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 this.state.delete(vaultId);\n throw err;\n }\n\n this.setState(vaultId, \"running\", containerName);\n await this.applyResourceLimits(containerName);\n return containerName;\n }\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 async remove(vaultId: string): Promise<void> {\n const containerName = this.getContainerName(vaultId);\n const networkName = DockerContainerManager.networkName(vaultId);\n\n try {\n await this.execFileImpl(\"docker\", [\"rm\", \"-f\", containerName]);\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 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(vaultId);\n }\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 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 const networkName = await this.ensureNetwork(vaultId);\n log.logInfo(`Creating container ${containerName} from image ${this.image}`);\n await this.execFileImpl(\"docker\", [\n \"run\",\n \"-d\",\n \"--name\",\n containerName,\n \"--network\",\n networkName,\n \"--label\",\n DockerContainerManager.MANAGED_LABEL,\n \"--label\",\n DockerContainerManager.IMAGE_MODE_LABEL,\n \"--label\",\n `${DockerContainerManager.VAULT_ID_LABEL_KEY}=${vaultId}`,\n ...this.resourceLimitArgs(),\n \"-v\",\n `${this.workspaceDir}:/workspace`,\n ...this.mountArgs(mounts),\n this.image,\n \"sleep\",\n \"infinity\",\n ]);\n }\n\n private resourceLimitArgs(): string[] {\n const args: string[] = [];\n if (this.limits?.cpus) args.push(\"--cpus\", this.limits.cpus);\n if (this.limits?.memory) args.push(\"--memory\", this.limits.memory);\n return args;\n }\n\n private async applyResourceLimits(containerName: string): Promise<void> {\n if (!this.limits?.cpus && !this.limits?.memory) return;\n const args = [\"update\", ...this.resourceLimitArgs(), 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 vaultId: string,\n containerName: string,\n mounts: ContainerMount[],\n ): Promise<boolean> {\n if (await this.hasBindMountDrift(containerName, mounts)) {\n return true;\n }\n return this.hasNetworkModeDrift(vaultId, 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 [`${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 hasNetworkModeDrift(vaultId: string, containerName: string): Promise<boolean> {\n const expected = DockerContainerManager.networkName(vaultId);\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(vaultId: string): Promise<string> {\n const networkName = DockerContainerManager.networkName(vaultId);\n try {\n await this.execFileImpl(\"docker\", [\"network\", \"inspect\", networkName]);\n } catch {\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}=${vaultId}`,\n networkName,\n ]);\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 {\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 { 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 ProvisionOptions {\n containerName?: string;\n mounts?: ContainerMount[];\n conversationId?: string;\n}\n\nexport interface DockerContainerManagerOptions {\n limits?: 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\n private readonly limits?: ResourceLimits;\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.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(containerName);\n return containerName;\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 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 }\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 await this.execFileImpl(\"docker\", [\n \"run\",\n \"-d\",\n \"--name\",\n containerName,\n \"--network\",\n networkName,\n ...labels,\n ...this.resourceLimitArgs(),\n ...this.mountArgs(mounts),\n this.image,\n \"sleep\",\n \"infinity\",\n ]);\n }\n\n private resourceLimitArgs(): string[] {\n const args: string[] = [];\n if (this.limits?.cpus) args.push(\"--cpus\", this.limits.cpus);\n if (this.limits?.memory) args.push(\"--memory\", this.limits.memory);\n return args;\n }\n\n private async applyResourceLimits(containerName: string): Promise<void> {\n if (!this.limits?.cpus && !this.limits?.memory) return;\n const args = [\"update\", ...this.resourceLimitArgs(), 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 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 .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 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\n/** @deprecated Use DockerContainerManager */\nexport const DockerProvisioner = DockerContainerManager;\n"]}
@@ -0,0 +1,2 @@
1
+ export { createSessionRuntime, type CreateSessionSandboxOptions, type RunSessionOptions, type SessionRuntime, type SessionRuntimeOptions, } from "./session-runtime.js";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -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,2 @@
1
+ export { createSessionRuntime, } from "./session-runtime.js";
2
+ //# sourceMappingURL=index.js.map
@@ -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,26 @@
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
+ isEvent?: 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
+ shutdown(timeoutMs?: number): Promise<void>;
24
+ }
25
+ export declare function createSessionRuntime(options: SessionRuntimeOptions): SessionRuntime;
26
+ //# 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,EACV,GAAG,EACH,WAAW,EACX,QAAQ,EACR,UAAU,EAGX,MAAM,eAAe,CAAC;AAMvB,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;AA0B5D,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,QAAQ,CAAC;IAChB,GAAG,EAAE,GAAG,CAAC;IACT,QAAQ,EAAE,WAAW,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;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,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7C;AAKD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,qBAAqB,GAAG,cAAc,CAEnF","sourcesContent":["import type {\n Bot,\n BotAdapters,\n BotEvent,\n BotHandler,\n PlatformName,\n RunningSession,\n} from \"../adapter.js\";\nimport {\n hasMaterializedSlackBranchSession,\n resolveSlackSessionScope,\n waitForSlackBranchBootstrap,\n} 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 { isPrivateConversation } from \"../commands/utils.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 { addLifecycleBreadcrumb, applyRunScope } from \"../sentry.js\";\nimport { formatNothingRunning, formatStopped, formatStopping } from \"../ui-copy.js\";\nimport * as Sentry from \"@sentry/node\";\nimport { join } from \"path\";\n\ninterface ConversationState {\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 RunSessionOptions {\n event: BotEvent;\n bot: Bot;\n adapters: BotAdapters;\n isEvent?: 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 shutdown(timeoutMs?: number): Promise<void>;\n}\n\nconst MAX_SESSIONS = 500;\nconst IDLE_TIMEOUT_MS = 3_600_000;\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 inFlightRuns = new Set<Promise<void>>();\n private readonly commandRegistry: CommandRegistry;\n private isShuttingDown = false;\n\n constructor(private readonly options: SessionRuntimeOptions) {\n this.commandRegistry = options.commandRegistry ?? createDefaultCommandRegistry();\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 handleNew(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 if (sessionKey.includes(\":\")) {\n createManagedSessionFileAtPath(\n getThreadSessionFile(conversationDir, sessionKey),\n conversationDir,\n );\n } else {\n createManagedSessionFile(getChannelSessionDir(conversationDir), conversationDir);\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 isEvent?: boolean,\n ): Promise<void> {\n await this.runSession({ event, bot, adapters, isEvent });\n }\n\n async runSession({ event, bot, adapters, isEvent }: RunSessionOptions): Promise<void> {\n const conversationId = event.conversationId;\n if (this.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.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,\n });\n if (handledCommand) return;\n\n const conversationDir = join(this.options.workingDir, conversationId);\n const waitedForParent =\n adapters.platform.name === \"slack\"\n ? await waitForSlackBranchBootstrap({\n parentSessionKey: conversationId,\n sessionKey,\n hasThreadSession: () => hasMaterializedSlackBranchSession(conversationDir, sessionKey),\n isParentRunning: () => this.conversationStates.get(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.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, isEvent, startedAt: state.startedAt! },\n async () => {\n await adapters.responseCtx.setTyping(true);\n await adapters.responseCtx.setWorking(true);\n const r = await state.runner.run(\n adapters.message,\n adapters.responseCtx,\n adapters.platform,\n );\n await adapters.responseCtx.setWorking(false);\n return r;\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 Sentry.metrics.gauge(\"agent.sessions.active\", this.inFlightRuns.size - 1);\n this.evictIdleSessions();\n }\n })();\n\n this.inFlightRuns.add(runPromise);\n try {\n await runPromise;\n } finally {\n this.inFlightRuns.delete(runPromise);\n }\n }\n\n private async runWithInstrumentation(\n adapters: BotAdapters,\n meta: { conversationId: string; sessionKey: string; isEvent?: boolean; startedAt: number },\n body: () => Promise<{ stopReason: string; errorMessage?: string }>,\n ): Promise<{ stopReason: string; errorMessage?: string } | undefined> {\n const { conversationId, sessionKey, isEvent, startedAt } = meta;\n const { message, platform } = adapters;\n\n Sentry.metrics.count(\"agent.run.started\", 1, {\n attributes: { channel: conversationId },\n });\n Sentry.metrics.gauge(\"agent.sessions.active\", this.inFlightRuns.size + 1);\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 isEvent,\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 async createSessionSandbox(options: CreateSessionSandboxOptions): Promise<AgentRunner> {\n const state = await this.getOrCreateState(options);\n return state.runner;\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 sessionScope = await this.resolveSessionScope(platformName, conversationDir, sessionKey);\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.bindingStore,\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 ): Promise<ResolvedSessionScope> {\n if (platformName === \"slack\") {\n return resolveSlackSessionScope({ conversationDir, sessionKey });\n }\n return resolveGenericSessionScope({ conversationDir, sessionKey });\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"]}