@geminixiang/mama 0.2.0-beta.6 → 0.2.0-beta.8

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 (173) hide show
  1. package/README.md +20 -14
  2. package/dist/adapter.d.ts +5 -2
  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 +29 -9
  8. package/dist/adapters/discord/bot.js.map +1 -1
  9. package/dist/adapters/discord/context.d.ts +1 -1
  10. package/dist/adapters/discord/context.d.ts.map +1 -1
  11. package/dist/adapters/discord/context.js +1 -2
  12. package/dist/adapters/discord/context.js.map +1 -1
  13. package/dist/adapters/slack/bot.d.ts +8 -0
  14. package/dist/adapters/slack/bot.d.ts.map +1 -1
  15. package/dist/adapters/slack/bot.js +177 -11
  16. package/dist/adapters/slack/bot.js.map +1 -1
  17. package/dist/adapters/slack/branch-manager.d.ts +1 -0
  18. package/dist/adapters/slack/branch-manager.d.ts.map +1 -1
  19. package/dist/adapters/slack/branch-manager.js +9 -8
  20. package/dist/adapters/slack/branch-manager.js.map +1 -1
  21. package/dist/adapters/slack/context.d.ts +1 -1
  22. package/dist/adapters/slack/context.d.ts.map +1 -1
  23. package/dist/adapters/slack/context.js +10 -8
  24. package/dist/adapters/slack/context.js.map +1 -1
  25. package/dist/adapters/slack/tools/attach.d.ts +1 -1
  26. package/dist/adapters/slack/tools/attach.d.ts.map +1 -1
  27. package/dist/adapters/slack/tools/attach.js.map +1 -1
  28. package/dist/adapters/telegram/bot.d.ts.map +1 -1
  29. package/dist/adapters/telegram/bot.js +33 -2
  30. package/dist/adapters/telegram/bot.js.map +1 -1
  31. package/dist/agent.d.ts +1 -2
  32. package/dist/agent.d.ts.map +1 -1
  33. package/dist/agent.js +507 -422
  34. package/dist/agent.js.map +1 -1
  35. package/dist/commands/index.d.ts.map +1 -1
  36. package/dist/commands/index.js +2 -0
  37. package/dist/commands/index.js.map +1 -1
  38. package/dist/commands/login.d.ts.map +1 -1
  39. package/dist/commands/login.js +41 -2
  40. package/dist/commands/login.js.map +1 -1
  41. package/dist/commands/model.d.ts +1 -1
  42. package/dist/commands/model.d.ts.map +1 -1
  43. package/dist/commands/model.js +25 -7
  44. package/dist/commands/model.js.map +1 -1
  45. package/dist/commands/new.d.ts.map +1 -1
  46. package/dist/commands/new.js +1 -1
  47. package/dist/commands/new.js.map +1 -1
  48. package/dist/commands/sandbox.d.ts +10 -0
  49. package/dist/commands/sandbox.d.ts.map +1 -0
  50. package/dist/commands/sandbox.js +88 -0
  51. package/dist/commands/sandbox.js.map +1 -0
  52. package/dist/commands/session-view.d.ts.map +1 -1
  53. package/dist/commands/session-view.js +34 -10
  54. package/dist/commands/session-view.js.map +1 -1
  55. package/dist/commands/types.d.ts +1 -3
  56. package/dist/commands/types.d.ts.map +1 -1
  57. package/dist/commands/types.js.map +1 -1
  58. package/dist/commands/utils.d.ts +3 -0
  59. package/dist/commands/utils.d.ts.map +1 -1
  60. package/dist/commands/utils.js +5 -0
  61. package/dist/commands/utils.js.map +1 -1
  62. package/dist/config.d.ts +7 -1
  63. package/dist/config.d.ts.map +1 -1
  64. package/dist/config.js +64 -23
  65. package/dist/config.js.map +1 -1
  66. package/dist/context.d.ts +2 -44
  67. package/dist/context.d.ts.map +1 -1
  68. package/dist/context.js +7 -210
  69. package/dist/context.js.map +1 -1
  70. package/dist/events.d.ts.map +1 -1
  71. package/dist/events.js +15 -14
  72. package/dist/events.js.map +1 -1
  73. package/dist/execution-resolver.d.ts +3 -2
  74. package/dist/execution-resolver.d.ts.map +1 -1
  75. package/dist/execution-resolver.js +40 -7
  76. package/dist/execution-resolver.js.map +1 -1
  77. package/dist/file-guards.d.ts +6 -0
  78. package/dist/file-guards.d.ts.map +1 -0
  79. package/dist/file-guards.js +48 -0
  80. package/dist/file-guards.js.map +1 -0
  81. package/dist/log.d.ts +1 -5
  82. package/dist/log.d.ts.map +1 -1
  83. package/dist/log.js +13 -38
  84. package/dist/log.js.map +1 -1
  85. package/dist/login/index.d.ts +14 -2
  86. package/dist/login/index.d.ts.map +1 -1
  87. package/dist/login/index.js +40 -13
  88. package/dist/login/index.js.map +1 -1
  89. package/dist/login/portal.d.ts +2 -1
  90. package/dist/login/portal.d.ts.map +1 -1
  91. package/dist/login/portal.js +12 -12
  92. package/dist/login/portal.js.map +1 -1
  93. package/dist/main.d.ts.map +1 -1
  94. package/dist/main.js +33 -28
  95. package/dist/main.js.map +1 -1
  96. package/dist/provisioner.d.ts +12 -2
  97. package/dist/provisioner.d.ts.map +1 -1
  98. package/dist/provisioner.js +43 -14
  99. package/dist/provisioner.js.map +1 -1
  100. package/dist/runtime/conversation-orchestrator.d.ts +42 -0
  101. package/dist/runtime/conversation-orchestrator.d.ts.map +1 -0
  102. package/dist/runtime/conversation-orchestrator.js +150 -0
  103. package/dist/runtime/conversation-orchestrator.js.map +1 -0
  104. package/dist/runtime/session-runtime.d.ts +1 -1
  105. package/dist/runtime/session-runtime.d.ts.map +1 -1
  106. package/dist/runtime/session-runtime.js +49 -148
  107. package/dist/runtime/session-runtime.js.map +1 -1
  108. package/dist/sandbox/cloudflare.d.ts.map +1 -1
  109. package/dist/sandbox/cloudflare.js +2 -2
  110. package/dist/sandbox/cloudflare.js.map +1 -1
  111. package/dist/sandbox/container.d.ts.map +1 -1
  112. package/dist/sandbox/container.js +1 -1
  113. package/dist/sandbox/container.js.map +1 -1
  114. package/dist/sandbox/index.d.ts.map +1 -1
  115. package/dist/sandbox/index.js +4 -4
  116. package/dist/sandbox/index.js.map +1 -1
  117. package/dist/sentry.d.ts +1 -1
  118. package/dist/sentry.d.ts.map +1 -1
  119. package/dist/sentry.js +2 -2
  120. package/dist/sentry.js.map +1 -1
  121. package/dist/session-store.d.ts +2 -1
  122. package/dist/session-store.d.ts.map +1 -1
  123. package/dist/session-store.js +19 -15
  124. package/dist/session-store.js.map +1 -1
  125. package/dist/session-view/portal.d.ts +6 -1
  126. package/dist/session-view/portal.d.ts.map +1 -1
  127. package/dist/session-view/portal.js +829 -71
  128. package/dist/session-view/portal.js.map +1 -1
  129. package/dist/session-view/service.d.ts.map +1 -1
  130. package/dist/session-view/service.js +5 -4
  131. package/dist/session-view/service.js.map +1 -1
  132. package/dist/session-view/store.d.ts +2 -1
  133. package/dist/session-view/store.d.ts.map +1 -1
  134. package/dist/session-view/store.js +2 -1
  135. package/dist/session-view/store.js.map +1 -1
  136. package/dist/store.d.ts.map +1 -1
  137. package/dist/store.js +7 -13
  138. package/dist/store.js.map +1 -1
  139. package/dist/tool-diagnostics.d.ts +2 -0
  140. package/dist/tool-diagnostics.d.ts.map +1 -0
  141. package/dist/tool-diagnostics.js +7 -0
  142. package/dist/tool-diagnostics.js.map +1 -0
  143. package/dist/tools/bash.d.ts +1 -1
  144. package/dist/tools/bash.d.ts.map +1 -1
  145. package/dist/tools/bash.js.map +1 -1
  146. package/dist/tools/edit.d.ts +1 -1
  147. package/dist/tools/edit.d.ts.map +1 -1
  148. package/dist/tools/edit.js.map +1 -1
  149. package/dist/tools/event.d.ts +1 -1
  150. package/dist/tools/event.d.ts.map +1 -1
  151. package/dist/tools/event.js.map +1 -1
  152. package/dist/tools/index.d.ts +1 -1
  153. package/dist/tools/index.d.ts.map +1 -1
  154. package/dist/tools/index.js.map +1 -1
  155. package/dist/tools/read.d.ts +1 -1
  156. package/dist/tools/read.d.ts.map +1 -1
  157. package/dist/tools/read.js.map +1 -1
  158. package/dist/tools/write.d.ts +1 -1
  159. package/dist/tools/write.d.ts.map +1 -1
  160. package/dist/tools/write.js.map +1 -1
  161. package/dist/vault-routing.d.ts +0 -3
  162. package/dist/vault-routing.d.ts.map +1 -1
  163. package/dist/vault-routing.js +0 -24
  164. package/dist/vault-routing.js.map +1 -1
  165. package/dist/vault.d.ts +21 -57
  166. package/dist/vault.d.ts.map +1 -1
  167. package/dist/vault.js +114 -246
  168. package/dist/vault.js.map +1 -1
  169. package/package.json +6 -4
  170. package/dist/bindings.d.ts +0 -45
  171. package/dist/bindings.d.ts.map +0 -1
  172. package/dist/bindings.js +0 -75
  173. package/dist/bindings.js.map +0 -1
@@ -1,8 +1,8 @@
1
- import { AuthStorage, ModelRegistry } from "@mariozechner/pi-coding-agent";
1
+ import { AuthStorage, ModelRegistry } from "@earendil-works/pi-coding-agent";
2
2
  import { homedir } from "os";
3
3
  import { join } from "path";
4
4
  import { loadAgentConfigForConversation, saveConversationModelConfig } from "../config.js";
5
- import { replyWithContext } from "./utils.js";
5
+ import { replyDiagnosticWithContext } from "./utils.js";
6
6
  const PI_AI_THINKING_LEVELS = [
7
7
  "minimal",
8
8
  "low",
@@ -53,6 +53,9 @@ function parseModelThinkingLevel(modelSpec) {
53
53
  function formatModelSpec(provider, model, thinkingLevel) {
54
54
  return `${provider}/${model}${thinkingLevel ? `:${thinkingLevel}` : ""}`;
55
55
  }
56
+ function formatModelCommandSummary(lines) {
57
+ return ["_Model_", ...lines].join("\n");
58
+ }
56
59
  export class ModelCommandHandler {
57
60
  async tryHandle(context) {
58
61
  const parsed = parseModelCommand(context.commandText);
@@ -61,20 +64,32 @@ export class ModelCommandHandler {
61
64
  const conversationDir = join(context.services.workingDir, context.conversationId);
62
65
  if (!parsed.provider || !parsed.model) {
63
66
  const current = loadAgentConfigForConversation(conversationDir);
64
- await replyWithContext(context.responseCtx, `目前模型:\`${formatModelSpec(current.provider, current.model, current.thinkingLevel)}\`\n用法:\`/pi-model provider/model[:thinking]\`,例如 \`/pi-model anthropic/claude-sonnet-4-5:off\`。`);
67
+ await replyDiagnosticWithContext(context.responseCtx, formatModelCommandSummary([
68
+ `Current: \`${formatModelSpec(current.provider, current.model, current.thinkingLevel)}\``,
69
+ "",
70
+ "Usage: `/pi-model provider/model[:thinking]`",
71
+ "Example: `/pi-model anthropic/claude-sonnet-4-5:off`",
72
+ ]), { style: "muted" });
65
73
  return true;
66
74
  }
67
75
  if (!this.isKnownModel(parsed.provider, parsed.model)) {
68
- await replyWithContext(context.responseCtx, `找不到模型 \`${formatModelSpec(parsed.provider, parsed.model, parsed.thinkingLevel)}\`。請確認 provider/model 名稱,或先在 pi models.json 註冊自訂模型。`);
76
+ await replyDiagnosticWithContext(context.responseCtx, formatModelCommandSummary([
77
+ `找不到模型:\`${formatModelSpec(parsed.provider, parsed.model, parsed.thinkingLevel)}\``,
78
+ "請確認 provider/model 名稱,或先在 pi models.json 註冊自訂模型。",
79
+ ]), { style: "muted" });
69
80
  return true;
70
81
  }
71
82
  if (!context.services.runtime) {
72
- await replyWithContext(context.responseCtx, "Model command is not configured correctly on the server. Please try again later.");
83
+ await replyDiagnosticWithContext(context.responseCtx, formatModelCommandSummary([
84
+ "Model command is not configured correctly on the server. Please try again later.",
85
+ ]), { style: "muted" });
73
86
  return true;
74
87
  }
75
88
  const switched = context.services.runtime.switchConversationModel(context.conversationId, parsed.provider, parsed.model);
76
89
  if (!switched) {
77
- await replyWithContext(context.responseCtx, "目前這個 conversation 有執行中的工作,請等它完成或先 `/stop` 後再切換模型。");
90
+ await replyDiagnosticWithContext(context.responseCtx, formatModelCommandSummary([
91
+ "目前這個 conversation 有執行中的工作,請等它完成或先 `/stop` 後再切換模型。",
92
+ ]), { style: "muted" });
78
93
  return true;
79
94
  }
80
95
  saveConversationModelConfig(conversationDir, {
@@ -82,7 +97,10 @@ export class ModelCommandHandler {
82
97
  model: parsed.model,
83
98
  ...(parsed.thinkingLevel ? { thinkingLevel: parsed.thinkingLevel } : {}),
84
99
  });
85
- await replyWithContext(context.responseCtx, `已切換這個 conversation 的模型為 \`${formatModelSpec(parsed.provider, parsed.model, parsed.thinkingLevel)}\`。下一則訊息會使用新模型。`);
100
+ await replyDiagnosticWithContext(context.responseCtx, formatModelCommandSummary([
101
+ `Switched: \`${formatModelSpec(parsed.provider, parsed.model, parsed.thinkingLevel)}\``,
102
+ "下一則訊息會使用新模型。",
103
+ ]), { style: "muted" });
86
104
  return true;
87
105
  }
88
106
  isKnownModel(provider, model) {
@@ -1 +1 @@
1
- {"version":3,"file":"model.js","sourceRoot":"","sources":["../../src/commands/model.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ThinkingLevel } from \"@mariozechner/pi-agent-core\";\nimport type { ThinkingLevel as PiAiThinkingLevel } from \"@mariozechner/pi-ai\";\nimport { AuthStorage, ModelRegistry } from \"@mariozechner/pi-coding-agent\";\nimport { homedir } from \"os\";\nimport { join } from \"path\";\nimport { loadAgentConfigForConversation, saveConversationModelConfig } from \"../config.js\";\nimport type { CommandContext, CommandHandler } from \"./types.js\";\nimport { replyWithContext } from \"./utils.js\";\n\nconst PI_AI_THINKING_LEVELS = [\n \"minimal\",\n \"low\",\n \"medium\",\n \"high\",\n \"xhigh\",\n] satisfies PiAiThinkingLevel[];\nconst THINKING_LEVELS = new Set<ThinkingLevel>([\"off\", ...PI_AI_THINKING_LEVELS]);\n\nexport interface ParsedModelCommand {\n command: \"model\" | \"/model\" | \"/pi-model\";\n provider?: string;\n model?: string;\n thinkingLevel?: ThinkingLevel;\n}\n\nexport function parseModelCommand(text: string): ParsedModelCommand | null {\n const tokens = text.trim().split(/\\s+/).filter(Boolean);\n if (tokens.length === 0) return null;\n\n const command = tokens[0].toLowerCase();\n if (command !== \"model\" && command !== \"/model\" && command !== \"/pi-model\") {\n return null;\n }\n\n if (tokens.length === 1) {\n return { command: command as ParsedModelCommand[\"command\"] };\n }\n\n const spec = tokens[1];\n const slash = spec.indexOf(\"/\");\n if (slash <= 0 || slash === spec.length - 1) {\n return { command: command as ParsedModelCommand[\"command\"] };\n }\n\n const modelSpec = spec.slice(slash + 1);\n const parsedModel = parseModelThinkingLevel(modelSpec);\n\n return {\n command: command as ParsedModelCommand[\"command\"],\n provider: spec.slice(0, slash),\n model: parsedModel.model,\n thinkingLevel: parsedModel.thinkingLevel,\n };\n}\n\nfunction parseModelThinkingLevel(modelSpec: string): {\n model: string;\n thinkingLevel?: ThinkingLevel;\n} {\n const colon = modelSpec.lastIndexOf(\":\");\n if (colon <= 0 || colon === modelSpec.length - 1) {\n return { model: modelSpec };\n }\n\n const suffix = modelSpec.slice(colon + 1);\n if (!THINKING_LEVELS.has(suffix as ThinkingLevel)) {\n return { model: modelSpec };\n }\n\n return {\n model: modelSpec.slice(0, colon),\n thinkingLevel: suffix as ThinkingLevel,\n };\n}\n\nfunction formatModelSpec(provider: string, model: string, thinkingLevel?: ThinkingLevel): string {\n return `${provider}/${model}${thinkingLevel ? `:${thinkingLevel}` : \"\"}`;\n}\n\nexport class ModelCommandHandler implements CommandHandler {\n async tryHandle(context: CommandContext): Promise<boolean> {\n const parsed = parseModelCommand(context.commandText);\n if (!parsed) return false;\n\n const conversationDir = join(context.services.workingDir, context.conversationId);\n if (!parsed.provider || !parsed.model) {\n const current = loadAgentConfigForConversation(conversationDir);\n await replyWithContext(\n context.responseCtx,\n `目前模型:\\`${formatModelSpec(current.provider, current.model, current.thinkingLevel)}\\`\\n用法:\\`/pi-model provider/model[:thinking]\\`,例如 \\`/pi-model anthropic/claude-sonnet-4-5:off\\`。`,\n );\n return true;\n }\n\n if (!this.isKnownModel(parsed.provider, parsed.model)) {\n await replyWithContext(\n context.responseCtx,\n `找不到模型 \\`${formatModelSpec(parsed.provider, parsed.model, parsed.thinkingLevel)}\\`。請確認 provider/model 名稱,或先在 pi models.json 註冊自訂模型。`,\n );\n return true;\n }\n\n if (!context.services.runtime) {\n await replyWithContext(\n context.responseCtx,\n \"Model command is not configured correctly on the server. Please try again later.\",\n );\n return true;\n }\n\n const switched = context.services.runtime.switchConversationModel(\n context.conversationId,\n parsed.provider,\n parsed.model,\n );\n if (!switched) {\n await replyWithContext(\n context.responseCtx,\n \"目前這個 conversation 有執行中的工作,請等它完成或先 `/stop` 後再切換模型。\",\n );\n return true;\n }\n\n saveConversationModelConfig(conversationDir, {\n provider: parsed.provider,\n model: parsed.model,\n ...(parsed.thinkingLevel ? { thinkingLevel: parsed.thinkingLevel } : {}),\n });\n\n await replyWithContext(\n context.responseCtx,\n `已切換這個 conversation 的模型為 \\`${formatModelSpec(parsed.provider, parsed.model, parsed.thinkingLevel)}\\`。下一則訊息會使用新模型。`,\n );\n return true;\n }\n\n private isKnownModel(provider: string, model: string): boolean {\n const authStorage = AuthStorage.create(join(homedir(), \".pi\", \"mama\", \"auth.json\"));\n const registry = ModelRegistry.create(authStorage);\n return registry.find(provider, model) !== undefined;\n }\n}\n"]}
1
+ {"version":3,"file":"model.js","sourceRoot":"","sources":["../../src/commands/model.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ThinkingLevel } from \"@earendil-works/pi-agent-core\";\nimport type { ThinkingLevel as PiAiThinkingLevel } from \"@earendil-works/pi-ai\";\nimport { AuthStorage, ModelRegistry } from \"@earendil-works/pi-coding-agent\";\nimport { homedir } from \"os\";\nimport { join } from \"path\";\nimport { loadAgentConfigForConversation, saveConversationModelConfig } from \"../config.js\";\nimport type { CommandContext, CommandHandler } from \"./types.js\";\nimport { replyDiagnosticWithContext } from \"./utils.js\";\n\nconst PI_AI_THINKING_LEVELS = [\n \"minimal\",\n \"low\",\n \"medium\",\n \"high\",\n \"xhigh\",\n] satisfies PiAiThinkingLevel[];\nconst THINKING_LEVELS = new Set<ThinkingLevel>([\"off\", ...PI_AI_THINKING_LEVELS]);\n\nexport interface ParsedModelCommand {\n command: \"model\" | \"/model\" | \"/pi-model\";\n provider?: string;\n model?: string;\n thinkingLevel?: ThinkingLevel;\n}\n\nexport function parseModelCommand(text: string): ParsedModelCommand | null {\n const tokens = text.trim().split(/\\s+/).filter(Boolean);\n if (tokens.length === 0) return null;\n\n const command = tokens[0].toLowerCase();\n if (command !== \"model\" && command !== \"/model\" && command !== \"/pi-model\") {\n return null;\n }\n\n if (tokens.length === 1) {\n return { command: command as ParsedModelCommand[\"command\"] };\n }\n\n const spec = tokens[1];\n const slash = spec.indexOf(\"/\");\n if (slash <= 0 || slash === spec.length - 1) {\n return { command: command as ParsedModelCommand[\"command\"] };\n }\n\n const modelSpec = spec.slice(slash + 1);\n const parsedModel = parseModelThinkingLevel(modelSpec);\n\n return {\n command: command as ParsedModelCommand[\"command\"],\n provider: spec.slice(0, slash),\n model: parsedModel.model,\n thinkingLevel: parsedModel.thinkingLevel,\n };\n}\n\nfunction parseModelThinkingLevel(modelSpec: string): {\n model: string;\n thinkingLevel?: ThinkingLevel;\n} {\n const colon = modelSpec.lastIndexOf(\":\");\n if (colon <= 0 || colon === modelSpec.length - 1) {\n return { model: modelSpec };\n }\n\n const suffix = modelSpec.slice(colon + 1);\n if (!THINKING_LEVELS.has(suffix as ThinkingLevel)) {\n return { model: modelSpec };\n }\n\n return {\n model: modelSpec.slice(0, colon),\n thinkingLevel: suffix as ThinkingLevel,\n };\n}\n\nfunction formatModelSpec(provider: string, model: string, thinkingLevel?: ThinkingLevel): string {\n return `${provider}/${model}${thinkingLevel ? `:${thinkingLevel}` : \"\"}`;\n}\n\nfunction formatModelCommandSummary(lines: string[]): string {\n return [\"_Model_\", ...lines].join(\"\\n\");\n}\n\nexport class ModelCommandHandler implements CommandHandler {\n async tryHandle(context: CommandContext): Promise<boolean> {\n const parsed = parseModelCommand(context.commandText);\n if (!parsed) return false;\n\n const conversationDir = join(context.services.workingDir, context.conversationId);\n if (!parsed.provider || !parsed.model) {\n const current = loadAgentConfigForConversation(conversationDir);\n await replyDiagnosticWithContext(\n context.responseCtx,\n formatModelCommandSummary([\n `Current: \\`${formatModelSpec(current.provider, current.model, current.thinkingLevel)}\\``,\n \"\",\n \"Usage: `/pi-model provider/model[:thinking]`\",\n \"Example: `/pi-model anthropic/claude-sonnet-4-5:off`\",\n ]),\n { style: \"muted\" },\n );\n return true;\n }\n\n if (!this.isKnownModel(parsed.provider, parsed.model)) {\n await replyDiagnosticWithContext(\n context.responseCtx,\n formatModelCommandSummary([\n `找不到模型:\\`${formatModelSpec(parsed.provider, parsed.model, parsed.thinkingLevel)}\\``,\n \"請確認 provider/model 名稱,或先在 pi models.json 註冊自訂模型。\",\n ]),\n { style: \"muted\" },\n );\n return true;\n }\n\n if (!context.services.runtime) {\n await replyDiagnosticWithContext(\n context.responseCtx,\n formatModelCommandSummary([\n \"Model command is not configured correctly on the server. Please try again later.\",\n ]),\n { style: \"muted\" },\n );\n return true;\n }\n\n const switched = context.services.runtime.switchConversationModel(\n context.conversationId,\n parsed.provider,\n parsed.model,\n );\n if (!switched) {\n await replyDiagnosticWithContext(\n context.responseCtx,\n formatModelCommandSummary([\n \"目前這個 conversation 有執行中的工作,請等它完成或先 `/stop` 後再切換模型。\",\n ]),\n { style: \"muted\" },\n );\n return true;\n }\n\n saveConversationModelConfig(conversationDir, {\n provider: parsed.provider,\n model: parsed.model,\n ...(parsed.thinkingLevel ? { thinkingLevel: parsed.thinkingLevel } : {}),\n });\n\n await replyDiagnosticWithContext(\n context.responseCtx,\n formatModelCommandSummary([\n `Switched: \\`${formatModelSpec(parsed.provider, parsed.model, parsed.thinkingLevel)}\\``,\n \"下一則訊息會使用新模型。\",\n ]),\n { style: \"muted\" },\n );\n return true;\n }\n\n private isKnownModel(provider: string, model: string): boolean {\n const authStorage = AuthStorage.create(join(homedir(), \".pi\", \"mama\", \"auth.json\"));\n const registry = ModelRegistry.create(authStorage);\n return registry.find(provider, model) !== undefined;\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"new.d.ts","sourceRoot":"","sources":["../../src/commands/new.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAGjE,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,KAAK,GAAG,MAAM,GAAG,SAAS,CAAC;CACrC;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAUrE;AAED,qBAAa,iBAAkB,YAAW,cAAc;IAChD,SAAS,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC,CAyBzD;CACF","sourcesContent":["import type { CommandContext, CommandHandler } from \"./types.js\";\nimport { replyWithContext } from \"./utils.js\";\n\nexport interface ParsedNewCommand {\n command: \"new\" | \"/new\" | \"/pi-new\";\n}\n\nexport function parseNewCommand(text: string): ParsedNewCommand | null {\n const tokens = text.trim().split(/\\s+/).filter(Boolean);\n if (tokens.length === 0) return null;\n\n const command = tokens[0].toLowerCase();\n if (command !== \"new\" && command !== \"/new\" && command !== \"/pi-new\") {\n return null;\n }\n\n return { command: command as ParsedNewCommand[\"command\"] };\n}\n\nexport class NewCommandHandler implements CommandHandler {\n async tryHandle(context: CommandContext): Promise<boolean> {\n if (!parseNewCommand(context.commandText)) return false;\n\n if (!context.privateConversation) {\n await replyWithContext(\n context.responseCtx,\n \"為了避免誤清除共享上下文,`/new` 目前只能在與機器人的私訊 / DM 中使用。\",\n );\n return true;\n }\n\n if (!context.services.runtime) {\n await replyWithContext(\n context.responseCtx,\n \"New command is not configured correctly on the server. Please try again later.\",\n );\n return true;\n }\n\n await context.services.runtime.handleNew(\n context.sessionKey,\n context.conversationId,\n context.bot,\n );\n return true;\n }\n}\n"]}
1
+ {"version":3,"file":"new.d.ts","sourceRoot":"","sources":["../../src/commands/new.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAGjE,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,KAAK,GAAG,MAAM,GAAG,SAAS,CAAC;CACrC;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAUrE;AAED,qBAAa,iBAAkB,YAAW,cAAc;IAChD,SAAS,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC,CAyBzD;CACF","sourcesContent":["import type { CommandContext, CommandHandler } from \"./types.js\";\nimport { replyWithContext } from \"./utils.js\";\n\nexport interface ParsedNewCommand {\n command: \"new\" | \"/new\" | \"/pi-new\";\n}\n\nexport function parseNewCommand(text: string): ParsedNewCommand | null {\n const tokens = text.trim().split(/\\s+/).filter(Boolean);\n if (tokens.length === 0) return null;\n\n const command = tokens[0].toLowerCase();\n if (command !== \"new\" && command !== \"/new\" && command !== \"/pi-new\") {\n return null;\n }\n\n return { command: command as ParsedNewCommand[\"command\"] };\n}\n\nexport class NewCommandHandler implements CommandHandler {\n async tryHandle(context: CommandContext): Promise<boolean> {\n if (!parseNewCommand(context.commandText)) return false;\n\n if (!context.privateConversation) {\n await replyWithContext(\n context.responseCtx,\n \"為了避免誤清除共享上下文,`/new` 目前只能在與機器人的私訊 / DM 中使用。\",\n );\n return true;\n }\n\n if (!context.services.runtime) {\n await replyWithContext(\n context.responseCtx,\n \"New command is not configured correctly on the server. Please try again later.\",\n );\n return true;\n }\n\n await context.services.runtime.handleNewCommand(\n context.sessionKey,\n context.conversationId,\n context.bot,\n );\n return true;\n }\n}\n"]}
@@ -21,7 +21,7 @@ export class NewCommandHandler {
21
21
  await replyWithContext(context.responseCtx, "New command is not configured correctly on the server. Please try again later.");
22
22
  return true;
23
23
  }
24
- await context.services.runtime.handleNew(context.sessionKey, context.conversationId, context.bot);
24
+ await context.services.runtime.handleNewCommand(context.sessionKey, context.conversationId, context.bot);
25
25
  return true;
26
26
  }
27
27
  }
@@ -1 +1 @@
1
- {"version":3,"file":"new.js","sourceRoot":"","sources":["../../src/commands/new.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAM9C,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACxD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAErC,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IACxC,IAAI,OAAO,KAAK,KAAK,IAAI,OAAO,KAAK,MAAM,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QACrE,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,OAAsC,EAAE,CAAC;AAC7D,CAAC;AAED,MAAM,OAAO,iBAAiB;IAC5B,KAAK,CAAC,SAAS,CAAC,OAAuB;QACrC,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,WAAW,CAAC;YAAE,OAAO,KAAK,CAAC;QAExD,IAAI,CAAC,OAAO,CAAC,mBAAmB,EAAE,CAAC;YACjC,MAAM,gBAAgB,CACpB,OAAO,CAAC,WAAW,EACnB,4CAA4C,CAC7C,CAAC;YACF,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;YAC9B,MAAM,gBAAgB,CACpB,OAAO,CAAC,WAAW,EACnB,gFAAgF,CACjF,CAAC;YACF,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,CACtC,OAAO,CAAC,UAAU,EAClB,OAAO,CAAC,cAAc,EACtB,OAAO,CAAC,GAAG,CACZ,CAAC;QACF,OAAO,IAAI,CAAC;IACd,CAAC;CACF","sourcesContent":["import type { CommandContext, CommandHandler } from \"./types.js\";\nimport { replyWithContext } from \"./utils.js\";\n\nexport interface ParsedNewCommand {\n command: \"new\" | \"/new\" | \"/pi-new\";\n}\n\nexport function parseNewCommand(text: string): ParsedNewCommand | null {\n const tokens = text.trim().split(/\\s+/).filter(Boolean);\n if (tokens.length === 0) return null;\n\n const command = tokens[0].toLowerCase();\n if (command !== \"new\" && command !== \"/new\" && command !== \"/pi-new\") {\n return null;\n }\n\n return { command: command as ParsedNewCommand[\"command\"] };\n}\n\nexport class NewCommandHandler implements CommandHandler {\n async tryHandle(context: CommandContext): Promise<boolean> {\n if (!parseNewCommand(context.commandText)) return false;\n\n if (!context.privateConversation) {\n await replyWithContext(\n context.responseCtx,\n \"為了避免誤清除共享上下文,`/new` 目前只能在與機器人的私訊 / DM 中使用。\",\n );\n return true;\n }\n\n if (!context.services.runtime) {\n await replyWithContext(\n context.responseCtx,\n \"New command is not configured correctly on the server. Please try again later.\",\n );\n return true;\n }\n\n await context.services.runtime.handleNew(\n context.sessionKey,\n context.conversationId,\n context.bot,\n );\n return true;\n }\n}\n"]}
1
+ {"version":3,"file":"new.js","sourceRoot":"","sources":["../../src/commands/new.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAM9C,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACxD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAErC,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IACxC,IAAI,OAAO,KAAK,KAAK,IAAI,OAAO,KAAK,MAAM,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QACrE,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,OAAsC,EAAE,CAAC;AAC7D,CAAC;AAED,MAAM,OAAO,iBAAiB;IAC5B,KAAK,CAAC,SAAS,CAAC,OAAuB;QACrC,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,WAAW,CAAC;YAAE,OAAO,KAAK,CAAC;QAExD,IAAI,CAAC,OAAO,CAAC,mBAAmB,EAAE,CAAC;YACjC,MAAM,gBAAgB,CACpB,OAAO,CAAC,WAAW,EACnB,4CAA4C,CAC7C,CAAC;YACF,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;YAC9B,MAAM,gBAAgB,CACpB,OAAO,CAAC,WAAW,EACnB,gFAAgF,CACjF,CAAC;YACF,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,gBAAgB,CAC7C,OAAO,CAAC,UAAU,EAClB,OAAO,CAAC,cAAc,EACtB,OAAO,CAAC,GAAG,CACZ,CAAC;QACF,OAAO,IAAI,CAAC;IACd,CAAC;CACF","sourcesContent":["import type { CommandContext, CommandHandler } from \"./types.js\";\nimport { replyWithContext } from \"./utils.js\";\n\nexport interface ParsedNewCommand {\n command: \"new\" | \"/new\" | \"/pi-new\";\n}\n\nexport function parseNewCommand(text: string): ParsedNewCommand | null {\n const tokens = text.trim().split(/\\s+/).filter(Boolean);\n if (tokens.length === 0) return null;\n\n const command = tokens[0].toLowerCase();\n if (command !== \"new\" && command !== \"/new\" && command !== \"/pi-new\") {\n return null;\n }\n\n return { command: command as ParsedNewCommand[\"command\"] };\n}\n\nexport class NewCommandHandler implements CommandHandler {\n async tryHandle(context: CommandContext): Promise<boolean> {\n if (!parseNewCommand(context.commandText)) return false;\n\n if (!context.privateConversation) {\n await replyWithContext(\n context.responseCtx,\n \"為了避免誤清除共享上下文,`/new` 目前只能在與機器人的私訊 / DM 中使用。\",\n );\n return true;\n }\n\n if (!context.services.runtime) {\n await replyWithContext(\n context.responseCtx,\n \"New command is not configured correctly on the server. Please try again later.\",\n );\n return true;\n }\n\n await context.services.runtime.handleNewCommand(\n context.sessionKey,\n context.conversationId,\n context.bot,\n );\n return true;\n }\n}\n"]}
@@ -0,0 +1,10 @@
1
+ import type { CommandContext, CommandHandler } from "./types.js";
2
+ export interface ParsedSandboxCommand {
3
+ command: "/pi-sandbox" | "/sandbox";
4
+ action?: "boost" | "private" | "full";
5
+ }
6
+ export declare function parseSandboxCommand(text: string): ParsedSandboxCommand | null;
7
+ export declare class SandboxCommandHandler implements CommandHandler {
8
+ tryHandle(context: CommandContext): Promise<boolean>;
9
+ }
10
+ //# sourceMappingURL=sandbox.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../../src/commands/sandbox.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAGjE,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,aAAa,GAAG,UAAU,CAAC;IACpC,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;CACvC;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,oBAAoB,GAAG,IAAI,CAc7E;AAMD,qBAAa,qBAAsB,YAAW,cAAc;IACpD,SAAS,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC,CA2FzD;CACF","sourcesContent":["import { join } from \"node:path\";\nimport { saveConversationSandboxConfig } from \"../config.js\";\nimport { readConversationWorkspaceMountMode } from \"../execution-resolver.js\";\nimport { resolveActorVaultKey } from \"../vault-routing.js\";\nimport type { CommandContext, CommandHandler } from \"./types.js\";\nimport { replyDiagnosticWithContext } from \"./utils.js\";\n\nexport interface ParsedSandboxCommand {\n command: \"/pi-sandbox\" | \"/sandbox\";\n action?: \"boost\" | \"private\" | \"full\";\n}\n\nexport function parseSandboxCommand(text: string): ParsedSandboxCommand | null {\n const tokens = text.trim().split(/\\s+/).filter(Boolean);\n if (tokens.length === 0) return null;\n\n const command = tokens[0].replace(/@\\w+$/i, \"\").toLowerCase();\n if (command !== \"/pi-sandbox\" && command !== \"/sandbox\") return null;\n if (tokens.length === 1) return { command };\n if (tokens.length === 2) {\n const action = tokens[1].toLowerCase();\n if (action === \"boost\" || action === \"private\" || action === \"full\") {\n return { command, action };\n }\n }\n return { command };\n}\n\nfunction formatSandboxCommandSummary(title: string, lines: string[]): string {\n return [`_${title}_`, ...lines].join(\"\\n\");\n}\n\nexport class SandboxCommandHandler implements CommandHandler {\n async tryHandle(context: CommandContext): Promise<boolean> {\n const parsed = parseSandboxCommand(context.commandText);\n if (!parsed) return false;\n\n if (context.services.sandbox.type !== \"image\" || !context.services.provisioner) {\n await replyDiagnosticWithContext(\n context.responseCtx,\n formatSandboxCommandSummary(\"Sandbox\", [\n \"`/pi-sandbox` 目前只支援 `image:*` managed sandbox。\",\n ]),\n { style: \"muted\" },\n );\n return true;\n }\n\n const containerKey = resolveActorVaultKey(\n context.services.sandbox,\n context.platformUserId,\n context.conversationId,\n );\n\n if (parsed.action === \"private\" || parsed.action === \"full\") {\n saveConversationSandboxConfig(join(context.services.workingDir, context.conversationId), {\n imageWorkspaceMount: parsed.action,\n });\n await replyDiagnosticWithContext(\n context.responseCtx,\n formatSandboxCommandSummary(\"Sandbox Workspace\", [\n parsed.action === \"full\"\n ? \"已將此 conversation 的 sandbox 設為 full workspace mode。\"\n : \"已將此 conversation 的 sandbox 設為 private workspace mode。\",\n `Workspace mount: ${parsed.action}`,\n parsed.action === \"full\"\n ? \"之後這個 container 會把整個 host workspace 掛到 /workspace。\"\n : \"之後這個 container 只會掛載 private workspace 檔案與當前 conversation 目錄。\",\n ]),\n { style: \"muted\" },\n );\n return true;\n }\n\n if (parsed.action === \"boost\") {\n const boostLimits = context.services.provisioner.getBoostLimits();\n if (!boostLimits?.cpus && !boostLimits?.memory) {\n await replyDiagnosticWithContext(\n context.responseCtx,\n formatSandboxCommandSummary(\"Sandbox Boost\", [\n \"此 mama instance 尚未設定 sandbox boost 規格。\",\n \"請先在全域 settings.json 設定 `sandbox.boost`。\",\n ]),\n { style: \"muted\" },\n );\n return true;\n }\n\n const status = await context.services.provisioner.boost(containerKey);\n await replyDiagnosticWithContext(\n context.responseCtx,\n formatSandboxCommandSummary(\"Sandbox Boost\", [\n \"已暫時提升此 conversation 的 sandbox 規格。\",\n `Current: ${formatLimits(status.limits)}`,\n \"boost 會在此 sandbox container 關閉後結束。\",\n ]),\n { style: \"muted\" },\n );\n return true;\n }\n\n const status = context.services.provisioner.getLimitStatus(containerKey);\n const defaultLimits = context.services.provisioner.getDefaultLimits();\n const boostLimits = context.services.provisioner.getBoostLimits();\n const workspaceMount = readConversationWorkspaceMountMode(\n context.services.workingDir,\n context.conversationId,\n );\n await replyDiagnosticWithContext(\n context.responseCtx,\n formatSandboxCommandSummary(\n \"Sandbox\",\n [\n `Current: ${formatLimits(status.limits)}`,\n `Status: ${status.boosted ? \"boosted\" : \"default\"}`,\n `Workspace mount: ${workspaceMount}`,\n \"\",\n `Default: ${formatLimits(defaultLimits)}`,\n boostLimits ? `Boost: ${formatLimits({ ...defaultLimits, ...boostLimits })}` : undefined,\n ].filter((line): line is string => line !== undefined),\n ),\n { style: \"muted\" },\n );\n return true;\n }\n}\n\nfunction formatLimits(limits: { cpus?: string; memory?: string } | undefined): string {\n return `CPU ${limits?.cpus ?? \"unlimited\"} / Memory ${limits?.memory ?? \"unlimited\"}`;\n}\n"]}
@@ -0,0 +1,88 @@
1
+ import { join } from "node:path";
2
+ import { saveConversationSandboxConfig } from "../config.js";
3
+ import { readConversationWorkspaceMountMode } from "../execution-resolver.js";
4
+ import { resolveActorVaultKey } from "../vault-routing.js";
5
+ import { replyDiagnosticWithContext } from "./utils.js";
6
+ export function parseSandboxCommand(text) {
7
+ const tokens = text.trim().split(/\s+/).filter(Boolean);
8
+ if (tokens.length === 0)
9
+ return null;
10
+ const command = tokens[0].replace(/@\w+$/i, "").toLowerCase();
11
+ if (command !== "/pi-sandbox" && command !== "/sandbox")
12
+ return null;
13
+ if (tokens.length === 1)
14
+ return { command };
15
+ if (tokens.length === 2) {
16
+ const action = tokens[1].toLowerCase();
17
+ if (action === "boost" || action === "private" || action === "full") {
18
+ return { command, action };
19
+ }
20
+ }
21
+ return { command };
22
+ }
23
+ function formatSandboxCommandSummary(title, lines) {
24
+ return [`_${title}_`, ...lines].join("\n");
25
+ }
26
+ export class SandboxCommandHandler {
27
+ async tryHandle(context) {
28
+ const parsed = parseSandboxCommand(context.commandText);
29
+ if (!parsed)
30
+ return false;
31
+ if (context.services.sandbox.type !== "image" || !context.services.provisioner) {
32
+ await replyDiagnosticWithContext(context.responseCtx, formatSandboxCommandSummary("Sandbox", [
33
+ "`/pi-sandbox` 目前只支援 `image:*` managed sandbox。",
34
+ ]), { style: "muted" });
35
+ return true;
36
+ }
37
+ const containerKey = resolveActorVaultKey(context.services.sandbox, context.platformUserId, context.conversationId);
38
+ if (parsed.action === "private" || parsed.action === "full") {
39
+ saveConversationSandboxConfig(join(context.services.workingDir, context.conversationId), {
40
+ imageWorkspaceMount: parsed.action,
41
+ });
42
+ await replyDiagnosticWithContext(context.responseCtx, formatSandboxCommandSummary("Sandbox Workspace", [
43
+ parsed.action === "full"
44
+ ? "已將此 conversation 的 sandbox 設為 full workspace mode。"
45
+ : "已將此 conversation 的 sandbox 設為 private workspace mode。",
46
+ `Workspace mount: ${parsed.action}`,
47
+ parsed.action === "full"
48
+ ? "之後這個 container 會把整個 host workspace 掛到 /workspace。"
49
+ : "之後這個 container 只會掛載 private workspace 檔案與當前 conversation 目錄。",
50
+ ]), { style: "muted" });
51
+ return true;
52
+ }
53
+ if (parsed.action === "boost") {
54
+ const boostLimits = context.services.provisioner.getBoostLimits();
55
+ if (!boostLimits?.cpus && !boostLimits?.memory) {
56
+ await replyDiagnosticWithContext(context.responseCtx, formatSandboxCommandSummary("Sandbox Boost", [
57
+ "此 mama instance 尚未設定 sandbox boost 規格。",
58
+ "請先在全域 settings.json 設定 `sandbox.boost`。",
59
+ ]), { style: "muted" });
60
+ return true;
61
+ }
62
+ const status = await context.services.provisioner.boost(containerKey);
63
+ await replyDiagnosticWithContext(context.responseCtx, formatSandboxCommandSummary("Sandbox Boost", [
64
+ "已暫時提升此 conversation 的 sandbox 規格。",
65
+ `Current: ${formatLimits(status.limits)}`,
66
+ "boost 會在此 sandbox container 關閉後結束。",
67
+ ]), { style: "muted" });
68
+ return true;
69
+ }
70
+ const status = context.services.provisioner.getLimitStatus(containerKey);
71
+ const defaultLimits = context.services.provisioner.getDefaultLimits();
72
+ const boostLimits = context.services.provisioner.getBoostLimits();
73
+ const workspaceMount = readConversationWorkspaceMountMode(context.services.workingDir, context.conversationId);
74
+ await replyDiagnosticWithContext(context.responseCtx, formatSandboxCommandSummary("Sandbox", [
75
+ `Current: ${formatLimits(status.limits)}`,
76
+ `Status: ${status.boosted ? "boosted" : "default"}`,
77
+ `Workspace mount: ${workspaceMount}`,
78
+ "",
79
+ `Default: ${formatLimits(defaultLimits)}`,
80
+ boostLimits ? `Boost: ${formatLimits({ ...defaultLimits, ...boostLimits })}` : undefined,
81
+ ].filter((line) => line !== undefined)), { style: "muted" });
82
+ return true;
83
+ }
84
+ }
85
+ function formatLimits(limits) {
86
+ return `CPU ${limits?.cpus ?? "unlimited"} / Memory ${limits?.memory ?? "unlimited"}`;
87
+ }
88
+ //# sourceMappingURL=sandbox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../../src/commands/sandbox.ts"],"names":[],"mappings":"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","sourcesContent":["import { join } from \"node:path\";\nimport { saveConversationSandboxConfig } from \"../config.js\";\nimport { readConversationWorkspaceMountMode } from \"../execution-resolver.js\";\nimport { resolveActorVaultKey } from \"../vault-routing.js\";\nimport type { CommandContext, CommandHandler } from \"./types.js\";\nimport { replyDiagnosticWithContext } from \"./utils.js\";\n\nexport interface ParsedSandboxCommand {\n command: \"/pi-sandbox\" | \"/sandbox\";\n action?: \"boost\" | \"private\" | \"full\";\n}\n\nexport function parseSandboxCommand(text: string): ParsedSandboxCommand | null {\n const tokens = text.trim().split(/\\s+/).filter(Boolean);\n if (tokens.length === 0) return null;\n\n const command = tokens[0].replace(/@\\w+$/i, \"\").toLowerCase();\n if (command !== \"/pi-sandbox\" && command !== \"/sandbox\") return null;\n if (tokens.length === 1) return { command };\n if (tokens.length === 2) {\n const action = tokens[1].toLowerCase();\n if (action === \"boost\" || action === \"private\" || action === \"full\") {\n return { command, action };\n }\n }\n return { command };\n}\n\nfunction formatSandboxCommandSummary(title: string, lines: string[]): string {\n return [`_${title}_`, ...lines].join(\"\\n\");\n}\n\nexport class SandboxCommandHandler implements CommandHandler {\n async tryHandle(context: CommandContext): Promise<boolean> {\n const parsed = parseSandboxCommand(context.commandText);\n if (!parsed) return false;\n\n if (context.services.sandbox.type !== \"image\" || !context.services.provisioner) {\n await replyDiagnosticWithContext(\n context.responseCtx,\n formatSandboxCommandSummary(\"Sandbox\", [\n \"`/pi-sandbox` 目前只支援 `image:*` managed sandbox。\",\n ]),\n { style: \"muted\" },\n );\n return true;\n }\n\n const containerKey = resolveActorVaultKey(\n context.services.sandbox,\n context.platformUserId,\n context.conversationId,\n );\n\n if (parsed.action === \"private\" || parsed.action === \"full\") {\n saveConversationSandboxConfig(join(context.services.workingDir, context.conversationId), {\n imageWorkspaceMount: parsed.action,\n });\n await replyDiagnosticWithContext(\n context.responseCtx,\n formatSandboxCommandSummary(\"Sandbox Workspace\", [\n parsed.action === \"full\"\n ? \"已將此 conversation 的 sandbox 設為 full workspace mode。\"\n : \"已將此 conversation 的 sandbox 設為 private workspace mode。\",\n `Workspace mount: ${parsed.action}`,\n parsed.action === \"full\"\n ? \"之後這個 container 會把整個 host workspace 掛到 /workspace。\"\n : \"之後這個 container 只會掛載 private workspace 檔案與當前 conversation 目錄。\",\n ]),\n { style: \"muted\" },\n );\n return true;\n }\n\n if (parsed.action === \"boost\") {\n const boostLimits = context.services.provisioner.getBoostLimits();\n if (!boostLimits?.cpus && !boostLimits?.memory) {\n await replyDiagnosticWithContext(\n context.responseCtx,\n formatSandboxCommandSummary(\"Sandbox Boost\", [\n \"此 mama instance 尚未設定 sandbox boost 規格。\",\n \"請先在全域 settings.json 設定 `sandbox.boost`。\",\n ]),\n { style: \"muted\" },\n );\n return true;\n }\n\n const status = await context.services.provisioner.boost(containerKey);\n await replyDiagnosticWithContext(\n context.responseCtx,\n formatSandboxCommandSummary(\"Sandbox Boost\", [\n \"已暫時提升此 conversation 的 sandbox 規格。\",\n `Current: ${formatLimits(status.limits)}`,\n \"boost 會在此 sandbox container 關閉後結束。\",\n ]),\n { style: \"muted\" },\n );\n return true;\n }\n\n const status = context.services.provisioner.getLimitStatus(containerKey);\n const defaultLimits = context.services.provisioner.getDefaultLimits();\n const boostLimits = context.services.provisioner.getBoostLimits();\n const workspaceMount = readConversationWorkspaceMountMode(\n context.services.workingDir,\n context.conversationId,\n );\n await replyDiagnosticWithContext(\n context.responseCtx,\n formatSandboxCommandSummary(\n \"Sandbox\",\n [\n `Current: ${formatLimits(status.limits)}`,\n `Status: ${status.boosted ? \"boosted\" : \"default\"}`,\n `Workspace mount: ${workspaceMount}`,\n \"\",\n `Default: ${formatLimits(defaultLimits)}`,\n boostLimits ? `Boost: ${formatLimits({ ...defaultLimits, ...boostLimits })}` : undefined,\n ].filter((line): line is string => line !== undefined),\n ),\n { style: \"muted\" },\n );\n return true;\n }\n}\n\nfunction formatLimits(limits: { cpus?: string; memory?: string } | undefined): string {\n return `CPU ${limits?.cpus ?? \"unlimited\"} / Memory ${limits?.memory ?? \"unlimited\"}`;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"session-view.d.ts","sourceRoot":"","sources":["../../src/commands/session-view.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAGjE,qBAAa,yBAA0B,YAAW,cAAc;IACxD,SAAS,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC,CAsDzD;CACF","sourcesContent":["import { resolveExistingSessionFile } from \"../session-view/service.js\";\nimport { parseSessionViewCommand } from \"../session-view/command.js\";\nimport type { CommandContext, CommandHandler } from \"./types.js\";\nimport { replyWithContext } from \"./utils.js\";\n\nexport class SessionViewCommandHandler implements CommandHandler {\n async tryHandle(context: CommandContext): Promise<boolean> {\n if (!parseSessionViewCommand(context.commandText)) return false;\n\n const sendSessionViewReply = async (text: string): Promise<void> => {\n if (context.privateConversation) {\n await replyWithContext(context.responseCtx, text);\n return;\n }\n\n if (context.bot.postPrivate) {\n await context.bot.postPrivate(context.conversationId, context.platformUserId, text);\n return;\n }\n\n await replyWithContext(context.responseCtx, text);\n };\n\n if (!context.privateConversation && !context.bot.postPrivate) {\n await sendSessionViewReply(\n \"為了保護對話內容,`/session` 目前只能在與機器人的私訊 / DM 中使用。\",\n );\n return true;\n }\n\n if (!context.services.portalBaseUrl) {\n await sendSessionViewReply(\n \"Session viewer is not configured. Set `MAMA_LINK_URL` or `MAMA_LINK_PORT` on the server.\",\n );\n return true;\n }\n\n const sessionFile = resolveExistingSessionFile(\n context.services.workingDir,\n context.conversationId,\n context.sessionKey,\n );\n if (!sessionFile) {\n await sendSessionViewReply(\n \"目前還沒有可查看的 session。先和機器人對話一次,建立 session 後再試。\",\n );\n return true;\n }\n\n const token = context.services.sessionViewTokenStore.create(\n context.platform,\n context.platformUserId,\n context.conversationId,\n context.sessionKey,\n sessionFile,\n );\n\n const linkText = `Open this read-only session link (expires in 24 hours):\\n${context.services.portalBaseUrl}/session?token=${token.token}`;\n await sendSessionViewReply(linkText);\n return true;\n }\n}\n"]}
1
+ {"version":3,"file":"session-view.d.ts","sourceRoot":"","sources":["../../src/commands/session-view.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAOjE,qBAAa,yBAA0B,YAAW,cAAc;IACxD,SAAS,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC,CA6EzD;CACF","sourcesContent":["import { resolveExistingSessionFile } from \"../session-view/service.js\";\nimport { parseSessionViewCommand } from \"../session-view/command.js\";\nimport type { CommandContext, CommandHandler } from \"./types.js\";\nimport { replyDiagnosticWithContext } from \"./utils.js\";\n\nfunction formatSessionCommandSummary(lines: string[]): string {\n return [\"_Session_\", ...lines].join(\"\\n\");\n}\n\nexport class SessionViewCommandHandler implements CommandHandler {\n async tryHandle(context: CommandContext): Promise<boolean> {\n if (!parseSessionViewCommand(context.commandText)) return false;\n\n const sendSessionViewReply = async (lines: string[]): Promise<void> => {\n const text = formatSessionCommandSummary(lines);\n if (context.privateConversation) {\n await replyDiagnosticWithContext(context.responseCtx, text, { style: \"muted\" });\n return;\n }\n\n if (context.bot.postPrivateDiagnostic) {\n await context.bot.postPrivateDiagnostic(\n context.conversationId,\n context.platformUserId,\n text,\n {\n style: \"muted\",\n },\n );\n return;\n }\n\n if (context.bot.postPrivate) {\n await context.bot.postPrivate(context.conversationId, context.platformUserId, text);\n return;\n }\n\n await replyDiagnosticWithContext(context.responseCtx, text, { style: \"muted\" });\n };\n\n if (!context.privateConversation && !context.bot.postPrivate) {\n await sendSessionViewReply([\n \"為了保護對話內容,`/session` 目前只能在與機器人的私訊 / DM 中使用。\",\n ]);\n return true;\n }\n\n if (!context.services.portalBaseUrl) {\n await sendSessionViewReply([\n \"Session viewer is not configured.\",\n \"Set `MAMA_LINK_URL` or `MAMA_LINK_PORT` on the server.\",\n ]);\n return true;\n }\n\n const sessionFile = resolveExistingSessionFile(\n context.services.workingDir,\n context.conversationId,\n context.sessionKey,\n );\n if (!sessionFile) {\n await sendSessionViewReply([\n \"目前還沒有可查看的 session。\",\n \"先和機器人對話一次,建立 session 後再試。\",\n ]);\n return true;\n }\n\n const platformUser = context.bot\n .getPlatformInfo()\n .users.find((user) => user.id === context.platformUserId);\n const platformUserName = platformUser?.userName || platformUser?.displayName;\n\n const token = context.services.sessionViewTokenStore.create(\n context.platform,\n context.platformUserId,\n context.conversationId,\n context.sessionKey,\n sessionFile,\n platformUserName,\n );\n\n await sendSessionViewReply([\n \"Open this read-only session link (expires in 24 hours):\",\n `${context.services.portalBaseUrl}/session?token=${token.token}`,\n ]);\n return true;\n }\n}\n"]}
@@ -1,37 +1,61 @@
1
1
  import { resolveExistingSessionFile } from "../session-view/service.js";
2
2
  import { parseSessionViewCommand } from "../session-view/command.js";
3
- import { replyWithContext } from "./utils.js";
3
+ import { replyDiagnosticWithContext } from "./utils.js";
4
+ function formatSessionCommandSummary(lines) {
5
+ return ["_Session_", ...lines].join("\n");
6
+ }
4
7
  export class SessionViewCommandHandler {
5
8
  async tryHandle(context) {
6
9
  if (!parseSessionViewCommand(context.commandText))
7
10
  return false;
8
- const sendSessionViewReply = async (text) => {
11
+ const sendSessionViewReply = async (lines) => {
12
+ const text = formatSessionCommandSummary(lines);
9
13
  if (context.privateConversation) {
10
- await replyWithContext(context.responseCtx, text);
14
+ await replyDiagnosticWithContext(context.responseCtx, text, { style: "muted" });
15
+ return;
16
+ }
17
+ if (context.bot.postPrivateDiagnostic) {
18
+ await context.bot.postPrivateDiagnostic(context.conversationId, context.platformUserId, text, {
19
+ style: "muted",
20
+ });
11
21
  return;
12
22
  }
13
23
  if (context.bot.postPrivate) {
14
24
  await context.bot.postPrivate(context.conversationId, context.platformUserId, text);
15
25
  return;
16
26
  }
17
- await replyWithContext(context.responseCtx, text);
27
+ await replyDiagnosticWithContext(context.responseCtx, text, { style: "muted" });
18
28
  };
19
29
  if (!context.privateConversation && !context.bot.postPrivate) {
20
- await sendSessionViewReply("為了保護對話內容,`/session` 目前只能在與機器人的私訊 / DM 中使用。");
30
+ await sendSessionViewReply([
31
+ "為了保護對話內容,`/session` 目前只能在與機器人的私訊 / DM 中使用。",
32
+ ]);
21
33
  return true;
22
34
  }
23
35
  if (!context.services.portalBaseUrl) {
24
- await sendSessionViewReply("Session viewer is not configured. Set `MAMA_LINK_URL` or `MAMA_LINK_PORT` on the server.");
36
+ await sendSessionViewReply([
37
+ "Session viewer is not configured.",
38
+ "Set `MAMA_LINK_URL` or `MAMA_LINK_PORT` on the server.",
39
+ ]);
25
40
  return true;
26
41
  }
27
42
  const sessionFile = resolveExistingSessionFile(context.services.workingDir, context.conversationId, context.sessionKey);
28
43
  if (!sessionFile) {
29
- await sendSessionViewReply("目前還沒有可查看的 session。先和機器人對話一次,建立 session 後再試。");
44
+ await sendSessionViewReply([
45
+ "目前還沒有可查看的 session。",
46
+ "先和機器人對話一次,建立 session 後再試。",
47
+ ]);
30
48
  return true;
31
49
  }
32
- const token = context.services.sessionViewTokenStore.create(context.platform, context.platformUserId, context.conversationId, context.sessionKey, sessionFile);
33
- const linkText = `Open this read-only session link (expires in 24 hours):\n${context.services.portalBaseUrl}/session?token=${token.token}`;
34
- await sendSessionViewReply(linkText);
50
+ const platformUser = context.bot
51
+ .getPlatformInfo()
52
+ .users.find((user) => user.id === context.platformUserId);
53
+ const platformUserName = platformUser?.userName || platformUser?.displayName;
54
+ const token = context.services.sessionViewTokenStore.create(context.platform, context.platformUserId, context.conversationId, context.sessionKey, sessionFile, platformUserName);
55
+ await sendSessionViewReply([
56
+ "Open this read-only session link (expires in 24 hours):",
57
+ `${context.services.portalBaseUrl}/session?token=${token.token}`,
58
+ ]);
35
59
  return true;
36
60
  }
37
61
  }
@@ -1 +1 @@
1
- {"version":3,"file":"session-view.js","sourceRoot":"","sources":["../../src/commands/session-view.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AACxE,OAAO,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAErE,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C,MAAM,OAAO,yBAAyB;IACpC,KAAK,CAAC,SAAS,CAAC,OAAuB;QACrC,IAAI,CAAC,uBAAuB,CAAC,OAAO,CAAC,WAAW,CAAC;YAAE,OAAO,KAAK,CAAC;QAEhE,MAAM,oBAAoB,GAAG,KAAK,EAAE,IAAY,EAAiB,EAAE;YACjE,IAAI,OAAO,CAAC,mBAAmB,EAAE,CAAC;gBAChC,MAAM,gBAAgB,CAAC,OAAO,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;gBAClD,OAAO;YACT,CAAC;YAED,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;gBAC5B,MAAM,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,OAAO,CAAC,cAAc,EAAE,OAAO,CAAC,cAAc,EAAE,IAAI,CAAC,CAAC;gBACpF,OAAO;YACT,CAAC;YAED,MAAM,gBAAgB,CAAC,OAAO,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;QACpD,CAAC,CAAC;QAEF,IAAI,CAAC,OAAO,CAAC,mBAAmB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;YAC7D,MAAM,oBAAoB,CACxB,4CAA4C,CAC7C,CAAC;YACF,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,aAAa,EAAE,CAAC;YACpC,MAAM,oBAAoB,CACxB,0FAA0F,CAC3F,CAAC;YACF,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,WAAW,GAAG,0BAA0B,CAC5C,OAAO,CAAC,QAAQ,CAAC,UAAU,EAC3B,OAAO,CAAC,cAAc,EACtB,OAAO,CAAC,UAAU,CACnB,CAAC;QACF,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,MAAM,oBAAoB,CACxB,6CAA6C,CAC9C,CAAC;YACF,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAC,MAAM,CACzD,OAAO,CAAC,QAAQ,EAChB,OAAO,CAAC,cAAc,EACtB,OAAO,CAAC,cAAc,EACtB,OAAO,CAAC,UAAU,EAClB,WAAW,CACZ,CAAC;QAEF,MAAM,QAAQ,GAAG,4DAA4D,OAAO,CAAC,QAAQ,CAAC,aAAa,kBAAkB,KAAK,CAAC,KAAK,EAAE,CAAC;QAC3I,MAAM,oBAAoB,CAAC,QAAQ,CAAC,CAAC;QACrC,OAAO,IAAI,CAAC;IACd,CAAC;CACF","sourcesContent":["import { resolveExistingSessionFile } from \"../session-view/service.js\";\nimport { parseSessionViewCommand } from \"../session-view/command.js\";\nimport type { CommandContext, CommandHandler } from \"./types.js\";\nimport { replyWithContext } from \"./utils.js\";\n\nexport class SessionViewCommandHandler implements CommandHandler {\n async tryHandle(context: CommandContext): Promise<boolean> {\n if (!parseSessionViewCommand(context.commandText)) return false;\n\n const sendSessionViewReply = async (text: string): Promise<void> => {\n if (context.privateConversation) {\n await replyWithContext(context.responseCtx, text);\n return;\n }\n\n if (context.bot.postPrivate) {\n await context.bot.postPrivate(context.conversationId, context.platformUserId, text);\n return;\n }\n\n await replyWithContext(context.responseCtx, text);\n };\n\n if (!context.privateConversation && !context.bot.postPrivate) {\n await sendSessionViewReply(\n \"為了保護對話內容,`/session` 目前只能在與機器人的私訊 / DM 中使用。\",\n );\n return true;\n }\n\n if (!context.services.portalBaseUrl) {\n await sendSessionViewReply(\n \"Session viewer is not configured. Set `MAMA_LINK_URL` or `MAMA_LINK_PORT` on the server.\",\n );\n return true;\n }\n\n const sessionFile = resolveExistingSessionFile(\n context.services.workingDir,\n context.conversationId,\n context.sessionKey,\n );\n if (!sessionFile) {\n await sendSessionViewReply(\n \"目前還沒有可查看的 session。先和機器人對話一次,建立 session 後再試。\",\n );\n return true;\n }\n\n const token = context.services.sessionViewTokenStore.create(\n context.platform,\n context.platformUserId,\n context.conversationId,\n context.sessionKey,\n sessionFile,\n );\n\n const linkText = `Open this read-only session link (expires in 24 hours):\\n${context.services.portalBaseUrl}/session?token=${token.token}`;\n await sendSessionViewReply(linkText);\n return true;\n }\n}\n"]}
1
+ {"version":3,"file":"session-view.js","sourceRoot":"","sources":["../../src/commands/session-view.ts"],"names":[],"mappings":"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","sourcesContent":["import { resolveExistingSessionFile } from \"../session-view/service.js\";\nimport { parseSessionViewCommand } from \"../session-view/command.js\";\nimport type { CommandContext, CommandHandler } from \"./types.js\";\nimport { replyDiagnosticWithContext } from \"./utils.js\";\n\nfunction formatSessionCommandSummary(lines: string[]): string {\n return [\"_Session_\", ...lines].join(\"\\n\");\n}\n\nexport class SessionViewCommandHandler implements CommandHandler {\n async tryHandle(context: CommandContext): Promise<boolean> {\n if (!parseSessionViewCommand(context.commandText)) return false;\n\n const sendSessionViewReply = async (lines: string[]): Promise<void> => {\n const text = formatSessionCommandSummary(lines);\n if (context.privateConversation) {\n await replyDiagnosticWithContext(context.responseCtx, text, { style: \"muted\" });\n return;\n }\n\n if (context.bot.postPrivateDiagnostic) {\n await context.bot.postPrivateDiagnostic(\n context.conversationId,\n context.platformUserId,\n text,\n {\n style: \"muted\",\n },\n );\n return;\n }\n\n if (context.bot.postPrivate) {\n await context.bot.postPrivate(context.conversationId, context.platformUserId, text);\n return;\n }\n\n await replyDiagnosticWithContext(context.responseCtx, text, { style: \"muted\" });\n };\n\n if (!context.privateConversation && !context.bot.postPrivate) {\n await sendSessionViewReply([\n \"為了保護對話內容,`/session` 目前只能在與機器人的私訊 / DM 中使用。\",\n ]);\n return true;\n }\n\n if (!context.services.portalBaseUrl) {\n await sendSessionViewReply([\n \"Session viewer is not configured.\",\n \"Set `MAMA_LINK_URL` or `MAMA_LINK_PORT` on the server.\",\n ]);\n return true;\n }\n\n const sessionFile = resolveExistingSessionFile(\n context.services.workingDir,\n context.conversationId,\n context.sessionKey,\n );\n if (!sessionFile) {\n await sendSessionViewReply([\n \"目前還沒有可查看的 session。\",\n \"先和機器人對話一次,建立 session 後再試。\",\n ]);\n return true;\n }\n\n const platformUser = context.bot\n .getPlatformInfo()\n .users.find((user) => user.id === context.platformUserId);\n const platformUserName = platformUser?.userName || platformUser?.displayName;\n\n const token = context.services.sessionViewTokenStore.create(\n context.platform,\n context.platformUserId,\n context.conversationId,\n context.sessionKey,\n sessionFile,\n platformUserName,\n );\n\n await sendSessionViewReply([\n \"Open this read-only session link (expires in 24 hours):\",\n `${context.services.portalBaseUrl}/session?token=${token.token}`,\n ]);\n return true;\n }\n}\n"]}
@@ -1,5 +1,4 @@
1
1
  import type { Bot, BotAdapters, PlatformName } from "../adapter.js";
2
- import type { UserBindingStore } from "../bindings.js";
3
2
  import type { DockerContainerManager } from "../provisioner.js";
4
3
  import type { SessionRuntime } from "../runtime/session-runtime.js";
5
4
  import type { SandboxConfig } from "../sandbox.js";
@@ -10,7 +9,7 @@ export interface LinkTokenStoreLike {
10
9
  };
11
10
  }
12
11
  export interface SessionViewTokenStoreLike {
13
- create(platform: PlatformName, platformUserId: string, conversationId: string, sessionKey: string, sessionFile: string): {
12
+ create(platform: PlatformName, platformUserId: string, conversationId: string, sessionKey: string, sessionFile: string, platformUserName?: string): {
14
13
  token: string;
15
14
  };
16
15
  }
@@ -19,7 +18,6 @@ export interface CommandServices {
19
18
  runtime?: SessionRuntime;
20
19
  sandbox: SandboxConfig;
21
20
  vaultManager: VaultManager;
22
- bindingStore?: UserBindingStore;
23
21
  provisioner?: DockerContainerManager;
24
22
  linkTokenStore: LinkTokenStoreLike;
25
23
  sessionViewTokenStore: SessionViewTokenStoreLike;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/commands/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACpE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AACpE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,WAAW,kBAAkB;IACjC,MAAM,CACJ,QAAQ,EAAE,YAAY,EACtB,cAAc,EAAE,MAAM,EACtB,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,GACjB;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CACtB;AAED,MAAM,WAAW,yBAAyB;IACxC,MAAM,CACJ,QAAQ,EAAE,YAAY,EACtB,cAAc,EAAE,MAAM,EACtB,cAAc,EAAE,MAAM,EACtB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,GAClB;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,OAAO,EAAE,aAAa,CAAC;IACvB,YAAY,EAAE,YAAY,CAAC;IAC3B,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC,WAAW,CAAC,EAAE,sBAAsB,CAAC;IACrC,cAAc,EAAE,kBAAkB,CAAC;IACnC,qBAAqB,EAAE,yBAAyB,CAAC;IACjD,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,GAAG,CAAC;IACT,WAAW,EAAE,WAAW,CAAC,aAAa,CAAC,CAAC;IACxC,QAAQ,EAAE,YAAY,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,QAAQ,EAAE,eAAe,CAAC;CAC3B;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACtD","sourcesContent":["import type { Bot, BotAdapters, PlatformName } from \"../adapter.js\";\nimport type { UserBindingStore } from \"../bindings.js\";\nimport type { DockerContainerManager } from \"../provisioner.js\";\nimport type { SessionRuntime } from \"../runtime/session-runtime.js\";\nimport type { SandboxConfig } from \"../sandbox.js\";\nimport type { VaultManager } from \"../vault.js\";\n\nexport interface LinkTokenStoreLike {\n create(\n platform: PlatformName,\n platformUserId: string,\n conversationId: string,\n vaultId: string,\n providerId: string,\n ): { token: string };\n}\n\nexport interface SessionViewTokenStoreLike {\n create(\n platform: PlatformName,\n platformUserId: string,\n conversationId: string,\n sessionKey: string,\n sessionFile: string,\n ): { token: string };\n}\n\nexport interface CommandServices {\n workingDir: string;\n runtime?: SessionRuntime;\n sandbox: SandboxConfig;\n vaultManager: VaultManager;\n bindingStore?: UserBindingStore;\n provisioner?: DockerContainerManager;\n linkTokenStore: LinkTokenStoreLike;\n sessionViewTokenStore: SessionViewTokenStoreLike;\n portalBaseUrl?: string;\n}\n\nexport interface CommandContext {\n bot: Bot;\n responseCtx: BotAdapters[\"responseCtx\"];\n platform: PlatformName;\n platformUserId: string;\n conversationId: string;\n vaultConversationId?: string;\n sessionKey: string;\n commandText: string;\n privateConversation: boolean;\n services: CommandServices;\n}\n\nexport interface CommandHandler {\n tryHandle(context: CommandContext): Promise<boolean>;\n}\n"]}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/commands/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACpE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AACpE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,WAAW,kBAAkB;IACjC,MAAM,CACJ,QAAQ,EAAE,YAAY,EACtB,cAAc,EAAE,MAAM,EACtB,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,GACjB;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CACtB;AAED,MAAM,WAAW,yBAAyB;IACxC,MAAM,CACJ,QAAQ,EAAE,YAAY,EACtB,cAAc,EAAE,MAAM,EACtB,cAAc,EAAE,MAAM,EACtB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,gBAAgB,CAAC,EAAE,MAAM,GACxB;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,OAAO,EAAE,aAAa,CAAC;IACvB,YAAY,EAAE,YAAY,CAAC;IAC3B,WAAW,CAAC,EAAE,sBAAsB,CAAC;IACrC,cAAc,EAAE,kBAAkB,CAAC;IACnC,qBAAqB,EAAE,yBAAyB,CAAC;IACjD,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,GAAG,CAAC;IACT,WAAW,EAAE,WAAW,CAAC,aAAa,CAAC,CAAC;IACxC,QAAQ,EAAE,YAAY,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,QAAQ,EAAE,eAAe,CAAC;CAC3B;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACtD","sourcesContent":["import type { Bot, BotAdapters, PlatformName } from \"../adapter.js\";\nimport type { DockerContainerManager } from \"../provisioner.js\";\nimport type { SessionRuntime } from \"../runtime/session-runtime.js\";\nimport type { SandboxConfig } from \"../sandbox.js\";\nimport type { VaultManager } from \"../vault.js\";\n\nexport interface LinkTokenStoreLike {\n create(\n platform: PlatformName,\n platformUserId: string,\n conversationId: string,\n vaultId: string,\n providerId: string,\n ): { token: string };\n}\n\nexport interface SessionViewTokenStoreLike {\n create(\n platform: PlatformName,\n platformUserId: string,\n conversationId: string,\n sessionKey: string,\n sessionFile: string,\n platformUserName?: string,\n ): { token: string };\n}\n\nexport interface CommandServices {\n workingDir: string;\n runtime?: SessionRuntime;\n sandbox: SandboxConfig;\n vaultManager: VaultManager;\n provisioner?: DockerContainerManager;\n linkTokenStore: LinkTokenStoreLike;\n sessionViewTokenStore: SessionViewTokenStoreLike;\n portalBaseUrl?: string;\n}\n\nexport interface CommandContext {\n bot: Bot;\n responseCtx: BotAdapters[\"responseCtx\"];\n platform: PlatformName;\n platformUserId: string;\n conversationId: string;\n vaultConversationId?: string;\n sessionKey: string;\n commandText: string;\n privateConversation: boolean;\n services: CommandServices;\n}\n\nexport interface CommandHandler {\n tryHandle(context: CommandContext): Promise<boolean>;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/commands/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { Bot, BotAdapters, PlatformName } from \"../adapter.js\";\nimport type { UserBindingStore } from \"../bindings.js\";\nimport type { DockerContainerManager } from \"../provisioner.js\";\nimport type { SessionRuntime } from \"../runtime/session-runtime.js\";\nimport type { SandboxConfig } from \"../sandbox.js\";\nimport type { VaultManager } from \"../vault.js\";\n\nexport interface LinkTokenStoreLike {\n create(\n platform: PlatformName,\n platformUserId: string,\n conversationId: string,\n vaultId: string,\n providerId: string,\n ): { token: string };\n}\n\nexport interface SessionViewTokenStoreLike {\n create(\n platform: PlatformName,\n platformUserId: string,\n conversationId: string,\n sessionKey: string,\n sessionFile: string,\n ): { token: string };\n}\n\nexport interface CommandServices {\n workingDir: string;\n runtime?: SessionRuntime;\n sandbox: SandboxConfig;\n vaultManager: VaultManager;\n bindingStore?: UserBindingStore;\n provisioner?: DockerContainerManager;\n linkTokenStore: LinkTokenStoreLike;\n sessionViewTokenStore: SessionViewTokenStoreLike;\n portalBaseUrl?: string;\n}\n\nexport interface CommandContext {\n bot: Bot;\n responseCtx: BotAdapters[\"responseCtx\"];\n platform: PlatformName;\n platformUserId: string;\n conversationId: string;\n vaultConversationId?: string;\n sessionKey: string;\n commandText: string;\n privateConversation: boolean;\n services: CommandServices;\n}\n\nexport interface CommandHandler {\n tryHandle(context: CommandContext): Promise<boolean>;\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/commands/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { Bot, BotAdapters, PlatformName } from \"../adapter.js\";\nimport type { DockerContainerManager } from \"../provisioner.js\";\nimport type { SessionRuntime } from \"../runtime/session-runtime.js\";\nimport type { SandboxConfig } from \"../sandbox.js\";\nimport type { VaultManager } from \"../vault.js\";\n\nexport interface LinkTokenStoreLike {\n create(\n platform: PlatformName,\n platformUserId: string,\n conversationId: string,\n vaultId: string,\n providerId: string,\n ): { token: string };\n}\n\nexport interface SessionViewTokenStoreLike {\n create(\n platform: PlatformName,\n platformUserId: string,\n conversationId: string,\n sessionKey: string,\n sessionFile: string,\n platformUserName?: string,\n ): { token: string };\n}\n\nexport interface CommandServices {\n workingDir: string;\n runtime?: SessionRuntime;\n sandbox: SandboxConfig;\n vaultManager: VaultManager;\n provisioner?: DockerContainerManager;\n linkTokenStore: LinkTokenStoreLike;\n sessionViewTokenStore: SessionViewTokenStoreLike;\n portalBaseUrl?: string;\n}\n\nexport interface CommandContext {\n bot: Bot;\n responseCtx: BotAdapters[\"responseCtx\"];\n platform: PlatformName;\n platformUserId: string;\n conversationId: string;\n vaultConversationId?: string;\n sessionKey: string;\n commandText: string;\n privateConversation: boolean;\n services: CommandServices;\n}\n\nexport interface CommandHandler {\n tryHandle(context: CommandContext): Promise<boolean>;\n}\n"]}
@@ -1,5 +1,8 @@
1
1
  import type { BotEvent } from "../adapter.js";
2
2
  import type { CommandContext } from "./types.js";
3
3
  export declare function replyWithContext(responseCtx: CommandContext["responseCtx"], text: string): Promise<void>;
4
+ export declare function replyDiagnosticWithContext(responseCtx: CommandContext["responseCtx"], text: string, options?: {
5
+ style?: "muted" | "error";
6
+ }): Promise<void>;
4
7
  export declare function isPrivateConversation(event: BotEvent): boolean;
5
8
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/commands/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,wBAAsB,gBAAgB,CACpC,WAAW,EAAE,cAAc,CAAC,aAAa,CAAC,EAC1C,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAIf;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAE9D","sourcesContent":["import type { BotEvent } from \"../adapter.js\";\nimport type { CommandContext } from \"./types.js\";\n\nexport async function replyWithContext(\n responseCtx: CommandContext[\"responseCtx\"],\n text: string,\n): Promise<void> {\n await responseCtx.setTyping(false);\n await responseCtx.setWorking(false);\n await responseCtx.respond(text);\n}\n\nexport function isPrivateConversation(event: BotEvent): boolean {\n return event.conversationKind === \"direct\" || event.type === \"dm\";\n}\n"]}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/commands/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,wBAAsB,gBAAgB,CACpC,WAAW,EAAE,cAAc,CAAC,aAAa,CAAC,EAC1C,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAIf;AAED,wBAAsB,0BAA0B,CAC9C,WAAW,EAAE,cAAc,CAAC,aAAa,CAAC,EAC1C,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO,CAAA;CAAE,GACtC,OAAO,CAAC,IAAI,CAAC,CAIf;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAE9D","sourcesContent":["import type { BotEvent } from \"../adapter.js\";\nimport type { CommandContext } from \"./types.js\";\n\nexport async function replyWithContext(\n responseCtx: CommandContext[\"responseCtx\"],\n text: string,\n): Promise<void> {\n await responseCtx.setTyping(false);\n await responseCtx.setWorking(false);\n await responseCtx.respond(text);\n}\n\nexport async function replyDiagnosticWithContext(\n responseCtx: CommandContext[\"responseCtx\"],\n text: string,\n options?: { style?: \"muted\" | \"error\" },\n): Promise<void> {\n await responseCtx.setTyping(false);\n await responseCtx.setWorking(false);\n await responseCtx.respondDiagnostic(text, options);\n}\n\nexport function isPrivateConversation(event: BotEvent): boolean {\n return event.conversationKind === \"direct\" || event.type === \"dm\";\n}\n"]}
@@ -3,6 +3,11 @@ export async function replyWithContext(responseCtx, text) {
3
3
  await responseCtx.setWorking(false);
4
4
  await responseCtx.respond(text);
5
5
  }
6
+ export async function replyDiagnosticWithContext(responseCtx, text, options) {
7
+ await responseCtx.setTyping(false);
8
+ await responseCtx.setWorking(false);
9
+ await responseCtx.respondDiagnostic(text, options);
10
+ }
6
11
  export function isPrivateConversation(event) {
7
12
  return event.conversationKind === "direct" || event.type === "dm";
8
13
  }
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/commands/utils.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,WAA0C,EAC1C,IAAY;IAEZ,MAAM,WAAW,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACnC,MAAM,WAAW,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IACpC,MAAM,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,KAAe;IACnD,OAAO,KAAK,CAAC,gBAAgB,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC;AACpE,CAAC","sourcesContent":["import type { BotEvent } from \"../adapter.js\";\nimport type { CommandContext } from \"./types.js\";\n\nexport async function replyWithContext(\n responseCtx: CommandContext[\"responseCtx\"],\n text: string,\n): Promise<void> {\n await responseCtx.setTyping(false);\n await responseCtx.setWorking(false);\n await responseCtx.respond(text);\n}\n\nexport function isPrivateConversation(event: BotEvent): boolean {\n return event.conversationKind === \"direct\" || event.type === \"dm\";\n}\n"]}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/commands/utils.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,WAA0C,EAC1C,IAAY;IAEZ,MAAM,WAAW,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACnC,MAAM,WAAW,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IACpC,MAAM,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AAClC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,WAA0C,EAC1C,IAAY,EACZ,OAAuC;IAEvC,MAAM,WAAW,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACnC,MAAM,WAAW,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IACpC,MAAM,WAAW,CAAC,iBAAiB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AACrD,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,KAAe;IACnD,OAAO,KAAK,CAAC,gBAAgB,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC;AACpE,CAAC","sourcesContent":["import type { BotEvent } from \"../adapter.js\";\nimport type { CommandContext } from \"./types.js\";\n\nexport async function replyWithContext(\n responseCtx: CommandContext[\"responseCtx\"],\n text: string,\n): Promise<void> {\n await responseCtx.setTyping(false);\n await responseCtx.setWorking(false);\n await responseCtx.respond(text);\n}\n\nexport async function replyDiagnosticWithContext(\n responseCtx: CommandContext[\"responseCtx\"],\n text: string,\n options?: { style?: \"muted\" | \"error\" },\n): Promise<void> {\n await responseCtx.setTyping(false);\n await responseCtx.setWorking(false);\n await responseCtx.respondDiagnostic(text, options);\n}\n\nexport function isPrivateConversation(event: BotEvent): boolean {\n return event.conversationKind === \"direct\" || event.type === \"dm\";\n}\n"]}
package/dist/config.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { ThinkingLevel } from "@mariozechner/pi-agent-core";
1
+ import type { ThinkingLevel } from "@earendil-works/pi-agent-core";
2
2
  export declare class MissingGlobalSettingsError extends Error {
3
3
  readonly settingsPath: string;
4
4
  constructor(settingsPath: string);
@@ -12,10 +12,16 @@ export interface AgentConfig {
12
12
  sentryDsn?: string;
13
13
  sandboxCpus?: string;
14
14
  sandboxMemory?: string;
15
+ sandboxBoostCpus?: string;
16
+ sandboxBoostMemory?: string;
17
+ sandboxImageWorkspaceMount?: "private" | "full";
15
18
  }
16
19
  export declare function loadAgentConfig(): AgentConfig;
17
20
  export declare function loadAgentConfigForConversation(conversationDir: string): AgentConfig;
18
21
  export declare function saveConversationModelConfig(conversationDir: string, config: Pick<AgentConfig, "provider" | "model"> & Partial<Pick<AgentConfig, "thinkingLevel">>): void;
22
+ export declare function saveConversationSandboxConfig(conversationDir: string, config: {
23
+ imageWorkspaceMount: AgentConfig["sandboxImageWorkspaceMount"];
24
+ }): void;
19
25
  export declare function resolveWorkspaceDirFromArgv(args?: string[]): string | undefined;
20
26
  export declare function resolveStateDirFromArgv(args?: string[]): string;
21
27
  export declare function resolveSentryDsn(): string | undefined;