@geminixiang/mikan 1.0.0-beta.37 → 1.0.0-beta.39

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 (201) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +1 -1
  3. package/dist/adapter.js +1 -1
  4. package/dist/adapter.js.map +1 -1
  5. package/dist/adapters/discord/bot.d.ts +3 -6
  6. package/dist/adapters/discord/bot.d.ts.map +1 -1
  7. package/dist/adapters/discord/bot.js +16 -39
  8. package/dist/adapters/discord/bot.js.map +1 -1
  9. package/dist/adapters/github/bot.d.ts +2 -0
  10. package/dist/adapters/github/bot.d.ts.map +1 -1
  11. package/dist/adapters/github/bot.js +15 -11
  12. package/dist/adapters/github/bot.js.map +1 -1
  13. package/dist/adapters/github/github-ops.d.ts +1 -1
  14. package/dist/adapters/github/github-ops.d.ts.map +1 -1
  15. package/dist/adapters/github/github-ops.js +2 -1
  16. package/dist/adapters/github/github-ops.js.map +1 -1
  17. package/dist/adapters/github/repo.d.ts +2 -1
  18. package/dist/adapters/github/repo.d.ts.map +1 -1
  19. package/dist/adapters/github/repo.js +2 -3
  20. package/dist/adapters/github/repo.js.map +1 -1
  21. package/dist/adapters/github/types.d.ts +1 -1
  22. package/dist/adapters/github/types.d.ts.map +1 -1
  23. package/dist/adapters/github/types.js.map +1 -1
  24. package/dist/adapters/intake.js +1 -1
  25. package/dist/adapters/intake.js.map +1 -1
  26. package/dist/adapters/shared.d.ts +17 -5
  27. package/dist/adapters/shared.d.ts.map +1 -1
  28. package/dist/adapters/shared.js +44 -8
  29. package/dist/adapters/shared.js.map +1 -1
  30. package/dist/adapters/slack/blocks.d.ts +14 -0
  31. package/dist/adapters/slack/blocks.d.ts.map +1 -1
  32. package/dist/adapters/slack/blocks.js +34 -0
  33. package/dist/adapters/slack/blocks.js.map +1 -1
  34. package/dist/adapters/slack/bot.d.ts +19 -6
  35. package/dist/adapters/slack/bot.d.ts.map +1 -1
  36. package/dist/adapters/slack/bot.js +88 -21
  37. package/dist/adapters/slack/bot.js.map +1 -1
  38. package/dist/adapters/slack/types.d.ts +1 -1
  39. package/dist/adapters/slack/types.js.map +1 -1
  40. package/dist/adapters/telegram/bot.d.ts +5 -6
  41. package/dist/adapters/telegram/bot.d.ts.map +1 -1
  42. package/dist/adapters/telegram/bot.js +29 -51
  43. package/dist/adapters/telegram/bot.js.map +1 -1
  44. package/dist/adapters/types.d.ts +19 -1
  45. package/dist/adapters/types.d.ts.map +1 -1
  46. package/dist/adapters/types.js.map +1 -1
  47. package/dist/agent.d.ts +0 -1
  48. package/dist/agent.d.ts.map +1 -1
  49. package/dist/agent.js +79 -101
  50. package/dist/agent.js.map +1 -1
  51. package/dist/cli/ext-dev.d.ts.map +1 -1
  52. package/dist/cli/ext-dev.js +8 -8
  53. package/dist/cli/ext-dev.js.map +1 -1
  54. package/dist/cli/ext.js +3 -3
  55. package/dist/cli/ext.js.map +1 -1
  56. package/dist/cli/office.js +2 -2
  57. package/dist/cli/office.js.map +1 -1
  58. package/dist/commands/auto-reply.d.ts.map +1 -1
  59. package/dist/commands/auto-reply.js +1 -2
  60. package/dist/commands/auto-reply.js.map +1 -1
  61. package/dist/commands/login.js +1 -1
  62. package/dist/commands/login.js.map +1 -1
  63. package/dist/commands/manifest.d.ts.map +1 -1
  64. package/dist/commands/manifest.js +3 -1
  65. package/dist/commands/manifest.js.map +1 -1
  66. package/dist/commands/model.d.ts.map +1 -1
  67. package/dist/commands/model.js +3 -7
  68. package/dist/commands/model.js.map +1 -1
  69. package/dist/commands/sandbox.d.ts.map +1 -1
  70. package/dist/commands/sandbox.js +7 -7
  71. package/dist/commands/sandbox.js.map +1 -1
  72. package/dist/commands/session-view.d.ts.map +1 -1
  73. package/dist/commands/session-view.js +1 -2
  74. package/dist/commands/session-view.js.map +1 -1
  75. package/dist/commands/types.d.ts +2 -1
  76. package/dist/commands/types.d.ts.map +1 -1
  77. package/dist/commands/types.js.map +1 -1
  78. package/dist/config.d.ts +10 -18
  79. package/dist/config.d.ts.map +1 -1
  80. package/dist/config.js +16 -15
  81. package/dist/config.js.map +1 -1
  82. package/dist/execution-resolver.d.ts +4 -6
  83. package/dist/execution-resolver.d.ts.map +1 -1
  84. package/dist/execution-resolver.js +10 -19
  85. package/dist/execution-resolver.js.map +1 -1
  86. package/dist/harness/extensions/loader.js +1 -1
  87. package/dist/harness/extensions/loader.js.map +1 -1
  88. package/dist/harness/skills.d.ts +8 -0
  89. package/dist/harness/skills.d.ts.map +1 -1
  90. package/dist/harness/skills.js +46 -4
  91. package/dist/harness/skills.js.map +1 -1
  92. package/dist/harness/types.d.ts +2 -0
  93. package/dist/harness/types.d.ts.map +1 -1
  94. package/dist/harness/types.js.map +1 -1
  95. package/dist/index.d.ts +2 -0
  96. package/dist/index.d.ts.map +1 -1
  97. package/dist/index.js +1 -0
  98. package/dist/index.js.map +1 -1
  99. package/dist/main.js +16 -16
  100. package/dist/main.js.map +1 -1
  101. package/dist/{office-address.d.ts → office/address.d.ts} +13 -14
  102. package/dist/office/address.d.ts.map +1 -0
  103. package/dist/{office-address.js → office/address.js} +13 -18
  104. package/dist/office/address.js.map +1 -0
  105. package/dist/office/index.d.ts +11 -0
  106. package/dist/office/index.d.ts.map +1 -0
  107. package/dist/office/index.js +11 -0
  108. package/dist/office/index.js.map +1 -0
  109. package/dist/office/layout.d.ts +20 -0
  110. package/dist/office/layout.d.ts.map +1 -0
  111. package/dist/office/layout.js +88 -0
  112. package/dist/office/layout.js.map +1 -0
  113. package/dist/{office-migration.d.ts → office/migration.d.ts} +3 -3
  114. package/dist/office/migration.d.ts.map +1 -0
  115. package/dist/{office-migration.js → office/migration.js} +8 -12
  116. package/dist/office/migration.js.map +1 -0
  117. package/dist/{office-registry.d.ts → office/registry.d.ts} +18 -24
  118. package/dist/office/registry.d.ts.map +1 -0
  119. package/dist/{office-registry.js → office/registry.js} +22 -83
  120. package/dist/office/registry.js.map +1 -0
  121. package/dist/office/types.d.ts +71 -0
  122. package/dist/office/types.d.ts.map +1 -0
  123. package/dist/office/types.js +2 -0
  124. package/dist/office/types.js.map +1 -0
  125. package/dist/packages/admin.js +5 -5
  126. package/dist/packages/admin.js.map +1 -1
  127. package/dist/packages/inspect.d.ts.map +1 -1
  128. package/dist/packages/inspect.js +3 -8
  129. package/dist/packages/inspect.js.map +1 -1
  130. package/dist/packages/materialize.js +1 -1
  131. package/dist/packages/materialize.js.map +1 -1
  132. package/dist/packages/resolve.js +5 -8
  133. package/dist/packages/resolve.js.map +1 -1
  134. package/dist/packages/types.d.ts +2 -5
  135. package/dist/packages/types.d.ts.map +1 -1
  136. package/dist/packages/types.js.map +1 -1
  137. package/dist/runtime/conversation-runtime.d.ts.map +1 -1
  138. package/dist/runtime/conversation-runtime.js +13 -15
  139. package/dist/runtime/conversation-runtime.js.map +1 -1
  140. package/dist/runtime/session-lifecycle.js +1 -1
  141. package/dist/runtime/session-lifecycle.js.map +1 -1
  142. package/dist/runtime/types.d.ts +3 -2
  143. package/dist/runtime/types.d.ts.map +1 -1
  144. package/dist/runtime/types.js.map +1 -1
  145. package/dist/sandbox/identity.js +1 -1
  146. package/dist/sandbox/identity.js.map +1 -1
  147. package/dist/settings-mutation.d.ts +3 -2
  148. package/dist/settings-mutation.d.ts.map +1 -1
  149. package/dist/settings-mutation.js +6 -7
  150. package/dist/settings-mutation.js.map +1 -1
  151. package/dist/tools/generate-image.d.ts +8 -2
  152. package/dist/tools/generate-image.d.ts.map +1 -1
  153. package/dist/tools/generate-image.js +5 -3
  154. package/dist/tools/generate-image.js.map +1 -1
  155. package/dist/tools/index.d.ts +5 -0
  156. package/dist/tools/index.d.ts.map +1 -1
  157. package/dist/tools/index.js +3 -5
  158. package/dist/tools/index.js.map +1 -1
  159. package/dist/trigger.d.ts +1 -1
  160. package/dist/trigger.d.ts.map +1 -1
  161. package/dist/trigger.js +5 -10
  162. package/dist/trigger.js.map +1 -1
  163. package/dist/types.d.ts +3 -22
  164. package/dist/types.d.ts.map +1 -1
  165. package/dist/types.js.map +1 -1
  166. package/dist/vault/index.js +1 -1
  167. package/dist/vault/index.js.map +1 -1
  168. package/dist/vault/injection.d.ts +3 -1
  169. package/dist/vault/injection.d.ts.map +1 -1
  170. package/dist/vault/injection.js +2 -2
  171. package/dist/vault/injection.js.map +1 -1
  172. package/dist/web/admin/portal.d.ts.map +1 -1
  173. package/dist/web/admin/portal.js +67 -80
  174. package/dist/web/admin/portal.js.map +1 -1
  175. package/dist/web/admin/types.d.ts +2 -1
  176. package/dist/web/admin/types.d.ts.map +1 -1
  177. package/dist/web/admin/types.js.map +1 -1
  178. package/dist/web/server.d.ts +1 -1
  179. package/dist/web/server.d.ts.map +1 -1
  180. package/dist/web/server.js +3 -3
  181. package/dist/web/server.js.map +1 -1
  182. package/dist/web/session-view/portal.js +1 -1
  183. package/dist/web/session-view/portal.js.map +1 -1
  184. package/dist/workspace-projection/index.d.ts +2 -4
  185. package/dist/workspace-projection/index.d.ts.map +1 -1
  186. package/dist/workspace-projection/index.js +21 -56
  187. package/dist/workspace-projection/index.js.map +1 -1
  188. package/dist/workspace-projection/types.d.ts +0 -2
  189. package/dist/workspace-projection/types.d.ts.map +1 -1
  190. package/dist/workspace-projection/types.js.map +1 -1
  191. package/package.json +41 -16
  192. package/dist/office-address.d.ts.map +0 -1
  193. package/dist/office-address.js.map +0 -1
  194. package/dist/office-migration.d.ts.map +0 -1
  195. package/dist/office-migration.js.map +0 -1
  196. package/dist/office-registry.d.ts.map +0 -1
  197. package/dist/office-registry.js.map +0 -1
  198. package/dist/store.d.ts +0 -51
  199. package/dist/store.d.ts.map +0 -1
  200. package/dist/store.js +0 -185
  201. package/dist/store.js.map +0 -1
@@ -1,85 +1,50 @@
1
- import { dirname, join } from "node:path";
1
+ import { dirname } from "node:path";
2
2
  import { lstatSync, mkdirSync, writeFileSync } from "node:fs";
3
3
  import { ensureDirExists } from "../utils/file-guards.js";
4
4
  import { resolveConversationSettings } from "../config.js";
5
- import { ensureOfficeDir } from "../office-registry.js";
6
5
  import * as log from "../log.js";
7
- import { conversationOfficeDir, officeDirName, validateOfficeAddress } from "../office-address.js";
8
6
  /**
9
7
  * The single policy seam for a managed office. It both materializes the
10
8
  * host-side roots and authorizes the prompt sources that describe them.
11
9
  * Legacy image settings are deliberately translated here, never at callers.
12
10
  */
13
- export function resolveWorkspaceProjection(hostWorkspaceRoot, address) {
14
- const normalized = validateOfficeAddress(address);
15
- const officeSegment = officeDirName(normalized);
16
- const conversationDir = hostWorkspaceRoot
17
- ? conversationOfficeDir(hostWorkspaceRoot, normalized)
18
- : join("/workspace", officeSegment);
19
- const effective = resolveEffectiveWorkspace(hostWorkspaceRoot, normalized, conversationDir);
20
- if (!hostWorkspaceRoot) {
21
- return {
22
- ...effective,
23
- mode: effective.layout === "full" ? "full" : "private",
24
- mounts: [],
25
- promptSources: {
26
- conversationDir,
27
- conversationMemoryPath: join(conversationDir, "MEMORY.md"),
28
- conversationSkillsDir: join(conversationDir, "skills"),
29
- ...(effective.layout === "shared-support" || effective.layout === "full"
30
- ? {
31
- globalMemoryPath: "/workspace/MEMORY.md",
32
- globalSkillsDir: "/workspace/skills",
33
- }
34
- : {}),
35
- },
36
- };
37
- }
38
- assertDirectory(hostWorkspaceRoot, "Host workspace root");
39
- ensureOfficeDir(hostWorkspaceRoot, normalized);
40
- const memoryPath = join(hostWorkspaceRoot, "MEMORY.md");
41
- const skillsPath = join(hostWorkspaceRoot, "skills");
42
- const eventsPath = join(hostWorkspaceRoot, "events");
11
+ export function resolveWorkspaceProjection(office) {
12
+ const { workspace } = office;
13
+ const effective = resolveEffectiveWorkspace(office);
14
+ assertDirectory(workspace.root, "Host workspace root");
15
+ office.ensure();
43
16
  const shared = effective.layout === "shared-support";
44
17
  if (shared) {
45
- ensureRegularFile(memoryPath, "Workspace memory");
46
- ensureDirectoryRoot(skillsPath, "Workspace skills");
47
- ensureDirectoryRoot(eventsPath, "Workspace events");
18
+ ensureRegularFile(workspace.memoryPath, "Workspace memory");
19
+ ensureDirectoryRoot(workspace.skillsDir, "Workspace skills");
20
+ ensureDirectoryRoot(workspace.eventsDir, "Workspace events");
48
21
  }
49
22
  const mounts = effective.layout === "full"
50
- ? [{ source: hostWorkspaceRoot, target: "/workspace" }]
23
+ ? [{ source: workspace.root, target: "/workspace" }]
51
24
  : effective.layout === "shared-support"
52
25
  ? [
53
- { source: memoryPath, target: "/workspace/MEMORY.md" },
54
- { source: skillsPath, target: "/workspace/skills" },
55
- { source: eventsPath, target: "/workspace/events" },
56
- { source: conversationDir, target: `/workspace/${officeSegment}` },
26
+ { source: workspace.memoryPath, target: "/workspace/MEMORY.md" },
27
+ { source: workspace.skillsDir, target: "/workspace/skills" },
28
+ { source: workspace.eventsDir, target: "/workspace/events" },
29
+ { source: office.dir, target: `/workspace/${office.key}` },
57
30
  ]
58
- : [{ source: conversationDir, target: `/workspace/${officeSegment}` }];
31
+ : [{ source: office.dir, target: `/workspace/${office.key}` }];
59
32
  return {
60
33
  ...effective,
61
- mode: effective.layout === "full" ? "full" : "private",
62
34
  mounts,
63
35
  promptSources: {
64
- conversationDir,
65
- conversationMemoryPath: join(conversationDir, "MEMORY.md"),
66
- conversationSkillsDir: join(conversationDir, "skills"),
36
+ conversationDir: office.dir,
37
+ conversationMemoryPath: office.memoryPath,
38
+ conversationSkillsDir: office.skillsDir,
67
39
  ...(effective.layout === "shared-support" || effective.layout === "full"
68
- ? { globalMemoryPath: memoryPath, globalSkillsDir: skillsPath }
40
+ ? { globalMemoryPath: workspace.memoryPath, globalSkillsDir: workspace.skillsDir }
69
41
  : {}),
70
42
  },
71
43
  };
72
44
  }
73
- /** Compatibility reader for old status callers. */
74
- export function readWorkspaceProjectionMode(hostWorkspaceRoot, address) {
75
- return resolveWorkspaceProjection(hostWorkspaceRoot, address).mode;
76
- }
77
- function resolveEffectiveWorkspace(hostWorkspaceRoot, address, conversationDir) {
78
- const fallback = { doorPolicy: "isolated", layout: "conversation" };
79
- if (!hostWorkspaceRoot)
80
- return fallback;
45
+ function resolveEffectiveWorkspace(office) {
81
46
  try {
82
- const settings = resolveConversationSettings({ address, conversationDir }).sandbox;
47
+ const settings = resolveConversationSettings(office).sandbox;
83
48
  if (settings?.workspace) {
84
49
  return normalizeWorkspace(settings.workspace.doorPolicy, settings.workspace.layout);
85
50
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/workspace-projection/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { dirname, join } from \"node:path\";\nimport { lstatSync, mkdirSync, writeFileSync } from \"node:fs\";\nimport { ensureDirExists } from \"../utils/file-guards.js\";\nimport { resolveConversationSettings } from \"../config.js\";\nimport { ensureOfficeDir } from \"../office-registry.js\";\nimport * as log from \"../log.js\";\nimport { conversationOfficeDir, officeDirName, validateOfficeAddress } from \"../office-address.js\";\nimport type {\n ImageWorkspaceMountMode,\n OfficeAddress,\n WorkspaceDoorPolicy,\n WorkspaceLayout,\n} from \"../types.js\";\nimport type { WorkspaceProjection } from \"./types.js\";\n\nexport type { WorkspaceProjection } from \"./types.js\";\n\n/**\n * The single policy seam for a managed office. It both materializes the\n * host-side roots and authorizes the prompt sources that describe them.\n * Legacy image settings are deliberately translated here, never at callers.\n */\nexport function resolveWorkspaceProjection(\n hostWorkspaceRoot: string | undefined,\n address: OfficeAddress,\n): WorkspaceProjection {\n const normalized = validateOfficeAddress(address);\n const officeSegment = officeDirName(normalized);\n const conversationDir = hostWorkspaceRoot\n ? conversationOfficeDir(hostWorkspaceRoot, normalized)\n : join(\"/workspace\", officeSegment);\n const effective = resolveEffectiveWorkspace(hostWorkspaceRoot, normalized, conversationDir);\n\n if (!hostWorkspaceRoot) {\n return {\n ...effective,\n mode: effective.layout === \"full\" ? \"full\" : \"private\",\n mounts: [],\n promptSources: {\n conversationDir,\n conversationMemoryPath: join(conversationDir, \"MEMORY.md\"),\n conversationSkillsDir: join(conversationDir, \"skills\"),\n ...(effective.layout === \"shared-support\" || effective.layout === \"full\"\n ? {\n globalMemoryPath: \"/workspace/MEMORY.md\",\n globalSkillsDir: \"/workspace/skills\",\n }\n : {}),\n },\n };\n }\n\n assertDirectory(hostWorkspaceRoot, \"Host workspace root\");\n ensureOfficeDir(hostWorkspaceRoot, normalized);\n const memoryPath = join(hostWorkspaceRoot, \"MEMORY.md\");\n const skillsPath = join(hostWorkspaceRoot, \"skills\");\n const eventsPath = join(hostWorkspaceRoot, \"events\");\n const shared = effective.layout === \"shared-support\";\n if (shared) {\n ensureRegularFile(memoryPath, \"Workspace memory\");\n ensureDirectoryRoot(skillsPath, \"Workspace skills\");\n ensureDirectoryRoot(eventsPath, \"Workspace events\");\n }\n\n const mounts =\n effective.layout === \"full\"\n ? [{ source: hostWorkspaceRoot, target: \"/workspace\" }]\n : effective.layout === \"shared-support\"\n ? [\n { source: memoryPath, target: \"/workspace/MEMORY.md\" },\n { source: skillsPath, target: \"/workspace/skills\" },\n { source: eventsPath, target: \"/workspace/events\" },\n { source: conversationDir, target: `/workspace/${officeSegment}` },\n ]\n : [{ source: conversationDir, target: `/workspace/${officeSegment}` }];\n\n return {\n ...effective,\n mode: effective.layout === \"full\" ? \"full\" : \"private\",\n mounts,\n promptSources: {\n conversationDir,\n conversationMemoryPath: join(conversationDir, \"MEMORY.md\"),\n conversationSkillsDir: join(conversationDir, \"skills\"),\n ...(effective.layout === \"shared-support\" || effective.layout === \"full\"\n ? { globalMemoryPath: memoryPath, globalSkillsDir: skillsPath }\n : {}),\n },\n };\n}\n\n/** Compatibility reader for old status callers. */\nexport function readWorkspaceProjectionMode(\n hostWorkspaceRoot: string | undefined,\n address: OfficeAddress,\n): ImageWorkspaceMountMode {\n return resolveWorkspaceProjection(hostWorkspaceRoot, address).mode;\n}\n\nfunction resolveEffectiveWorkspace(\n hostWorkspaceRoot: string | undefined,\n address: OfficeAddress,\n conversationDir: string,\n): Pick<WorkspaceProjection, \"doorPolicy\" | \"layout\"> {\n const fallback = { doorPolicy: \"isolated\" as const, layout: \"conversation\" as const };\n if (!hostWorkspaceRoot) return fallback;\n\n try {\n const settings = resolveConversationSettings({ address, conversationDir }).sandbox;\n if (settings?.workspace) {\n return normalizeWorkspace(settings.workspace.doorPolicy, settings.workspace.layout);\n }\n return legacyWorkspace(settings?.image?.workspaceMount);\n } catch (err) {\n // Settings are host-authoritative. A malformed file must not fall back to\n // an unvalidated value from a mounted legacy location.\n log.logWarning(\n \"Refusing to resolve workspace projection from malformed settings\",\n err instanceof Error ? err.message : String(err),\n );\n throw new Error(\"Cannot resolve workspace projection: settings are malformed\", { cause: err });\n }\n}\n\nfunction normalizeWorkspace(\n doorPolicy: WorkspaceDoorPolicy | undefined,\n layout: WorkspaceLayout | undefined,\n): Pick<WorkspaceProjection, \"doorPolicy\" | \"layout\"> {\n const policy = doorPolicy ?? \"isolated\";\n if (policy === \"isolated\") return { doorPolicy: policy, layout: \"conversation\" };\n return { doorPolicy: policy, layout: layout === \"full\" ? \"full\" : \"shared-support\" };\n}\n\nfunction legacyWorkspace(\n mode: ImageWorkspaceMountMode | undefined,\n): Pick<WorkspaceProjection, \"doorPolicy\" | \"layout\"> {\n return mode === \"full\"\n ? { doorPolicy: \"trusted\", layout: \"full\" }\n : mode === \"private\"\n ? { doorPolicy: \"trusted\", layout: \"shared-support\" }\n : { doorPolicy: \"isolated\", layout: \"conversation\" };\n}\n\nfunction ensureDirectoryRoot(path: string, label: string): void {\n if (!exists(path)) {\n mkdirSync(path, { recursive: true, mode: 0o700 });\n return;\n }\n assertDirectory(path, label);\n}\n\nfunction ensureRegularFile(path: string, label: string): void {\n if (!exists(path)) {\n ensureDirExists(dirname(path));\n writeFileSync(path, \"\", { mode: 0o600, flag: \"wx\" });\n return;\n }\n let stats;\n try {\n stats = lstatSync(path);\n } catch (err) {\n throw new Error(`${label} cannot be inspected: ${path}`, { cause: err });\n }\n if (stats.isSymbolicLink() || !stats.isFile()) {\n throw new Error(`${label} must be a regular non-symlink file: ${path}`);\n }\n}\n\nfunction assertDirectory(path: string, label: string): void {\n let stats;\n try {\n stats = lstatSync(path);\n } catch (err) {\n throw new Error(`${label} cannot be inspected: ${path}`, { cause: err });\n }\n if (stats.isSymbolicLink() || !stats.isDirectory()) {\n throw new Error(`${label} must be a regular non-symlink directory: ${path}`);\n }\n}\n\nfunction exists(path: string): boolean {\n try {\n lstatSync(path);\n return true;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return false;\n throw err;\n }\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/workspace-projection/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { dirname } from \"node:path\";\nimport { lstatSync, mkdirSync, writeFileSync } from \"node:fs\";\nimport { ensureDirExists } from \"../utils/file-guards.js\";\nimport { resolveConversationSettings } from \"../config.js\";\nimport type { Office } from \"../office/index.js\";\nimport * as log from \"../log.js\";\nimport type { ImageWorkspaceMountMode, WorkspaceDoorPolicy, WorkspaceLayout } from \"../types.js\";\nimport type { WorkspaceProjection } from \"./types.js\";\n\nexport type { WorkspaceProjection } from \"./types.js\";\n\n/**\n * The single policy seam for a managed office. It both materializes the\n * host-side roots and authorizes the prompt sources that describe them.\n * Legacy image settings are deliberately translated here, never at callers.\n */\nexport function resolveWorkspaceProjection(office: Office): WorkspaceProjection {\n const { workspace } = office;\n const effective = resolveEffectiveWorkspace(office);\n\n assertDirectory(workspace.root, \"Host workspace root\");\n office.ensure();\n const shared = effective.layout === \"shared-support\";\n if (shared) {\n ensureRegularFile(workspace.memoryPath, \"Workspace memory\");\n ensureDirectoryRoot(workspace.skillsDir, \"Workspace skills\");\n ensureDirectoryRoot(workspace.eventsDir, \"Workspace events\");\n }\n\n const mounts =\n effective.layout === \"full\"\n ? [{ source: workspace.root, target: \"/workspace\" }]\n : effective.layout === \"shared-support\"\n ? [\n { source: workspace.memoryPath, target: \"/workspace/MEMORY.md\" },\n { source: workspace.skillsDir, target: \"/workspace/skills\" },\n { source: workspace.eventsDir, target: \"/workspace/events\" },\n { source: office.dir, target: `/workspace/${office.key}` },\n ]\n : [{ source: office.dir, target: `/workspace/${office.key}` }];\n\n return {\n ...effective,\n mounts,\n promptSources: {\n conversationDir: office.dir,\n conversationMemoryPath: office.memoryPath,\n conversationSkillsDir: office.skillsDir,\n ...(effective.layout === \"shared-support\" || effective.layout === \"full\"\n ? { globalMemoryPath: workspace.memoryPath, globalSkillsDir: workspace.skillsDir }\n : {}),\n },\n };\n}\n\nfunction resolveEffectiveWorkspace(\n office: Office,\n): Pick<WorkspaceProjection, \"doorPolicy\" | \"layout\"> {\n try {\n const settings = resolveConversationSettings(office).sandbox;\n if (settings?.workspace) {\n return normalizeWorkspace(settings.workspace.doorPolicy, settings.workspace.layout);\n }\n return legacyWorkspace(settings?.image?.workspaceMount);\n } catch (err) {\n // Settings are host-authoritative. A malformed file must not fall back to\n // an unvalidated value from a mounted legacy location.\n log.logWarning(\n \"Refusing to resolve workspace projection from malformed settings\",\n err instanceof Error ? err.message : String(err),\n );\n throw new Error(\"Cannot resolve workspace projection: settings are malformed\", { cause: err });\n }\n}\n\nfunction normalizeWorkspace(\n doorPolicy: WorkspaceDoorPolicy | undefined,\n layout: WorkspaceLayout | undefined,\n): Pick<WorkspaceProjection, \"doorPolicy\" | \"layout\"> {\n const policy = doorPolicy ?? \"isolated\";\n if (policy === \"isolated\") return { doorPolicy: policy, layout: \"conversation\" };\n return { doorPolicy: policy, layout: layout === \"full\" ? \"full\" : \"shared-support\" };\n}\n\nfunction legacyWorkspace(\n mode: ImageWorkspaceMountMode | undefined,\n): Pick<WorkspaceProjection, \"doorPolicy\" | \"layout\"> {\n return mode === \"full\"\n ? { doorPolicy: \"trusted\", layout: \"full\" }\n : mode === \"private\"\n ? { doorPolicy: \"trusted\", layout: \"shared-support\" }\n : { doorPolicy: \"isolated\", layout: \"conversation\" };\n}\n\nfunction ensureDirectoryRoot(path: string, label: string): void {\n if (!exists(path)) {\n mkdirSync(path, { recursive: true, mode: 0o700 });\n return;\n }\n assertDirectory(path, label);\n}\n\nfunction ensureRegularFile(path: string, label: string): void {\n if (!exists(path)) {\n ensureDirExists(dirname(path));\n writeFileSync(path, \"\", { mode: 0o600, flag: \"wx\" });\n return;\n }\n let stats;\n try {\n stats = lstatSync(path);\n } catch (err) {\n throw new Error(`${label} cannot be inspected: ${path}`, { cause: err });\n }\n if (stats.isSymbolicLink() || !stats.isFile()) {\n throw new Error(`${label} must be a regular non-symlink file: ${path}`);\n }\n}\n\nfunction assertDirectory(path: string, label: string): void {\n let stats;\n try {\n stats = lstatSync(path);\n } catch (err) {\n throw new Error(`${label} cannot be inspected: ${path}`, { cause: err });\n }\n if (stats.isSymbolicLink() || !stats.isDirectory()) {\n throw new Error(`${label} must be a regular non-symlink directory: ${path}`);\n }\n}\n\nfunction exists(path: string): boolean {\n try {\n lstatSync(path);\n return true;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return false;\n throw err;\n }\n}\n"]}
@@ -9,8 +9,6 @@ interface WorkspacePromptSources {
9
9
  export interface WorkspaceProjection {
10
10
  doorPolicy: WorkspaceDoorPolicy;
11
11
  layout: WorkspaceLayout;
12
- /** Legacy layout name for callers that only display the old setting. */
13
- mode: "private" | "full";
14
12
  mounts: ContainerMount[];
15
13
  promptSources: WorkspacePromptSources;
16
14
  }
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/workspace-projection/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAExF,UAAU,sBAAsB;IAC9B,eAAe,EAAE,MAAM,CAAC;IACxB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,mBAAmB,CAAC;IAChC,MAAM,EAAE,eAAe,CAAC;IACxB,wEAAwE;IACxE,IAAI,EAAE,SAAS,GAAG,MAAM,CAAC;IACzB,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,aAAa,EAAE,sBAAsB,CAAC;CACvC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/workspace-projection/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAExF,UAAU,sBAAsB;IAC9B,eAAe,EAAE,MAAM,CAAC;IACxB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,mBAAmB,CAAC;IAChC,MAAM,EAAE,eAAe,CAAC;IACxB,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,aAAa,EAAE,sBAAsB,CAAC;CACvC"}
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/workspace-projection/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { ContainerMount, WorkspaceDoorPolicy, WorkspaceLayout } from \"../types.js\";\n\ninterface WorkspacePromptSources {\n conversationDir: string;\n conversationMemoryPath: string;\n conversationSkillsDir: string;\n globalMemoryPath?: string;\n globalSkillsDir?: string;\n}\n\nexport interface WorkspaceProjection {\n doorPolicy: WorkspaceDoorPolicy;\n layout: WorkspaceLayout;\n /** Legacy layout name for callers that only display the old setting. */\n mode: \"private\" | \"full\";\n mounts: ContainerMount[];\n promptSources: WorkspacePromptSources;\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/workspace-projection/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { ContainerMount, WorkspaceDoorPolicy, WorkspaceLayout } from \"../types.js\";\n\ninterface WorkspacePromptSources {\n conversationDir: string;\n conversationMemoryPath: string;\n conversationSkillsDir: string;\n globalMemoryPath?: string;\n globalSkillsDir?: string;\n}\n\nexport interface WorkspaceProjection {\n doorPolicy: WorkspaceDoorPolicy;\n layout: WorkspaceLayout;\n mounts: ContainerMount[];\n promptSources: WorkspacePromptSources;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geminixiang/mikan",
3
- "version": "1.0.0-beta.37",
3
+ "version": "1.0.0-beta.39",
4
4
  "description": "Multi-platform AI coding agent for Slack, Telegram, Discord, and GitHub",
5
5
  "keywords": [
6
6
  "agent",
@@ -43,26 +43,26 @@
43
43
  },
44
44
  "scripts": {
45
45
  "clean": "shx rm -rf dist",
46
- "build": "npm run clean && tsgo -p tsconfig.build.json && shx chmod +x dist/main.js",
47
- "dev": "tsgo -p tsconfig.build.json --watch --preserveWatchOutput",
48
- "test": "vitest --run",
49
- "test:coverage": "vitest --run --coverage",
50
- "test:coverage:check": "vitest --run --coverage",
46
+ "build": "npm run clean && tsgo -p src/tsconfig.build.json && shx chmod +x dist/main.js",
47
+ "dev": "tsgo -p src/tsconfig.build.json --watch --preserveWatchOutput",
48
+ "test": "vitest --run --config .config/vitest.config.ts",
49
+ "test:coverage": "vitest --run --coverage --config .config/vitest.config.ts",
50
+ "test:coverage:check": "vitest --run --coverage --config .config/vitest.config.ts",
51
51
  "test:office:docker": "node scripts/verify-office-docker.mjs",
52
- "lint": "oxlint",
53
- "lint:fix": "oxlint --fix",
54
- "fmt": "oxfmt",
55
- "fmt:check": "oxfmt --check",
52
+ "lint": "oxlint -c .config/oxlintrc.json",
53
+ "lint:fix": "oxlint -c .config/oxlintrc.json --fix",
54
+ "fmt": "oxfmt -c .config/oxfmtrc.json",
55
+ "fmt:check": "oxfmt -c .config/oxfmtrc.json --check",
56
56
  "knip": "knip",
57
57
  "prepublishOnly": "npm run build",
58
58
  "prepare": "husky",
59
- "test:e2e": "vitest --run --config vitest.e2e.config.ts",
60
- "test:e2e:slack": "vitest --run --config vitest.e2e.config.ts e2e/slack",
61
- "test:e2e:slack:coverage": "vitest --run --coverage --coverage.reportsDirectory=.workspace/coverage --config vitest.e2e.config.ts e2e/slack",
59
+ "test:e2e": "vitest --run --config .config/vitest.e2e.config.ts",
60
+ "test:e2e:slack": "vitest --run --config .config/vitest.e2e.config.ts e2e/slack",
61
+ "test:e2e:slack:coverage": "vitest --run --coverage --coverage.reportsDirectory=.workspace/coverage --config .config/vitest.e2e.config.ts e2e/slack",
62
62
  "gondolin:image:build": "sh scripts/build-gondolin-image.sh",
63
- "docs:dev": "astro dev",
64
- "docs:build": "astro build",
65
- "docs:preview": "astro preview"
63
+ "docs:dev": "astro dev --config .config/astro.config.mjs",
64
+ "docs:build": "astro build --config .config/astro.config.mjs",
65
+ "docs:preview": "astro preview --config .config/astro.config.mjs"
66
66
  },
67
67
  "dependencies": {
68
68
  "@earendil-works/gondolin": "0.12.0",
@@ -122,5 +122,30 @@
122
122
  },
123
123
  "engines": {
124
124
  "node": ">=22.19.0"
125
+ },
126
+ "knip": {
127
+ "entry": [
128
+ "src/index.ts",
129
+ "src/main.ts",
130
+ "src/cli/download.ts",
131
+ "src/observability/instrument.ts",
132
+ "src/harness/index.ts",
133
+ "src/sandbox/index.ts"
134
+ ],
135
+ "ignore": [
136
+ "scripts/**",
137
+ "deploy/**"
138
+ ],
139
+ "astro": {
140
+ "config": [
141
+ ".config/astro.config.mjs"
142
+ ]
143
+ },
144
+ "vitest": {
145
+ "config": [
146
+ ".config/vitest.config.ts",
147
+ ".config/vitest.e2e.config.ts"
148
+ ]
149
+ }
125
150
  }
126
151
  }
@@ -1 +0,0 @@
1
- {"version":3,"file":"office-address.d.ts","sourceRoot":"","sources":["../src/office-address.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAQzE,wEAAwE;AACxE,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,GAAG,aAAa,CAIjG;AAED,yEAAyE;AACzE,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,aAAa,CASnE;AAOD,gEAAgE;AAChE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,CAG9D;AAED,gFAAgF;AAChF,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAiB1D;AAED,oFAAoF;AACpF,wBAAgB,SAAS,CAAC,OAAO,EAAE,aAAa,GAAG,SAAS,CAQ3D;AAED,gEAAgE;AAChE,wBAAgB,SAAS,CAAC,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,MAAM,CAE/E;AAED,2DAA2D;AAC3D,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,MAAM,CAE/E;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CAE5D;AAED,oEAAoE;AACpE,wBAAgB,qBAAqB,CAAC,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,MAAM,CAE3F;AAED,iFAAiF;AACjF,wBAAgB,UAAU,CAAC,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,aAAa,GAAG,OAAO,CAO7E;AAED,8DAA8D;AAC9D,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAIxD;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,SAAS,CAE9D"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"office-address.js","sourceRoot":"","sources":["../src/office-address.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,MAAM,kBAAkB,GAAG,IAAI,CAAC;AAChC,MAAM,iBAAiB,GAAG,mBAAmB,CAAC;AAC9C,MAAM,wBAAwB,GAAG,EAAE,CAAC;AACpC,MAAM,kBAAkB,GAAG,EAAE,CAAC;AAC9B,MAAM,cAAc,GAAG,IAAI,GAAG,CAAe,CAAC,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC;AAEzF,wEAAwE;AACxE,MAAM,UAAU,mBAAmB,CAAC,QAAsB,EAAE,cAAsB;IAChF,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IAC7B,oBAAoB,CAAC,cAAc,CAAC,CAAC;IACrC,OAAO,MAAM,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,cAAc,EAAE,CAAC,CAAC;AACrD,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,qBAAqB,CAAC,KAAc;IAClD,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;IAC1E,IAAI,OAAO,KAAK,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACvC,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;IAC9D,CAAC;IACD,IAAI,OAAO,KAAK,CAAC,cAAc,KAAK,QAAQ,EAAE,CAAC;QAC7C,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;IACrE,CAAC;IACD,OAAO,mBAAmB,CAAC,kBAAkB,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC;AACvF,CAAC;AAED,sDAAsD;AACtD,SAAS,cAAc,CAAC,KAAc;IACpC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,cAAc,CAAC,GAAG,CAAC,KAAqB,CAAC,CAAC;AAChF,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,kBAAkB,CAAC,KAAa;IAC9C,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC9F,OAAO,KAAK,CAAC;AACf,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,oBAAoB,CAAC,KAAa;IAChD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,KAAK,GAAG,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAC1D,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;IAC7E,CAAC;IACD,KAAK,MAAM,SAAS,IAAI,KAAK,EAAE,CAAC;QAC9B,MAAM,IAAI,GAAG,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC3C,IACE,SAAS,KAAK,GAAG;YACjB,SAAS,KAAK,IAAI;YAClB,IAAI,KAAK,CAAC;YACV,IAAI,IAAI,IAAI;YACZ,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC,EAC9B,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,wEAAwE,CAAC,CAAC;QAC5F,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,SAAS,CAAC,OAAsB;IAC9C,MAAM,UAAU,GAAG,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAClD,MAAM,QAAQ,GAAG,sBAAsB,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;IACnE,MAAM,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC;SAChC,MAAM,CAAC,GAAG,iBAAiB,KAAK,UAAU,CAAC,QAAQ,KAAK,UAAU,CAAC,cAAc,EAAE,CAAC;SACpF,MAAM,CAAC,KAAK,CAAC;SACb,KAAK,CAAC,CAAC,EAAE,wBAAwB,CAAC,CAAC;IACtC,OAAO,GAAG,kBAAkB,IAAI,UAAU,CAAC,QAAQ,IAAI,QAAQ,IAAI,MAAM,EAAe,CAAC;AAC3F,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,SAAS,CAAC,aAAqB,EAAE,OAAsB;IACrE,OAAO,IAAI,CAAC,aAAa,EAAE,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;AACjD,CAAC;AAED,2DAA2D;AAC3D,MAAM,UAAU,cAAc,CAAC,QAAgB,EAAE,OAAsB;IACrE,OAAO,IAAI,CAAC,QAAQ,EAAE,eAAe,EAAE,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;AAC7D,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAAC,OAAsB;IAClD,OAAO,SAAS,CAAC,OAAO,CAAC,CAAC;AAC5B,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,qBAAqB,CAAC,aAAqB,EAAE,OAAsB;IACjF,OAAO,IAAI,CAAC,aAAa,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC;AACrD,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,UAAU,CAAC,IAAmB,EAAE,KAAoB;IAClE,MAAM,WAAW,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;IAChD,MAAM,YAAY,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAC;IAClD,OAAO,CACL,WAAW,CAAC,QAAQ,KAAK,YAAY,CAAC,QAAQ;QAC9C,WAAW,CAAC,cAAc,KAAK,YAAY,CAAC,cAAc,CAC3D,CAAC;AACJ,CAAC;AAED,8DAA8D;AAC9D,MAAM,UAAU,eAAe,CAAC,KAAa;IAC3C,MAAM,OAAO,GAAG,4EAA4E,CAAC;IAC7F,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC1F,OAAO,KAAkB,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,uBAAuB,CAAC,KAAK,CAAC,CAAC;AACrE,CAAC;AAED,SAAS,sBAAsB,CAAC,cAAsB;IACpD,MAAM,QAAQ,GAAG,cAAc;SAC5B,WAAW,EAAE;SACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,KAAK,CAAC,CAAC,EAAE,kBAAkB,CAAC;SAC5B,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACvB,OAAO,QAAQ,IAAI,cAAc,CAAC;AACpC,CAAC;AAED,SAAS,uBAAuB,CAAC,KAAa;IAC5C,OAAO,4EAA4E,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAClG,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { join } from \"node:path\";\nimport type { OfficeAddress, OfficeKey, PlatformName } from \"./types.js\";\n\nconst OFFICE_KEY_VERSION = \"v1\";\nconst OFFICE_KEY_DOMAIN = \"office-address-v1\";\nconst OFFICE_KEY_DIGEST_LENGTH = 16;\nconst READABLE_ID_LENGTH = 32;\nconst PLATFORM_NAMES = new Set<PlatformName>([\"slack\", \"discord\", \"telegram\", \"github\"]);\n\n/** Construct the canonical identity used by future office consumers. */\nexport function createOfficeAddress(platform: PlatformName, conversationId: string): OfficeAddress {\n assertPlatformName(platform);\n assertConversationId(conversationId);\n return Object.freeze({ platform, conversationId });\n}\n\n/** Validate an untrusted runtime value as a canonical office address. */\nexport function validateOfficeAddress(value: unknown): OfficeAddress {\n if (!isRecord(value)) throw new Error(\"Office address must be an object\");\n if (typeof value.platform !== \"string\") {\n throw new Error(\"Office address platform must be a string\");\n }\n if (typeof value.conversationId !== \"string\") {\n throw new Error(\"Office address conversation id must be a string\");\n }\n return createOfficeAddress(assertPlatformName(value.platform), value.conversationId);\n}\n\n/** Return true only for a supported platform name. */\nfunction isPlatformName(value: unknown): value is PlatformName {\n return typeof value === \"string\" && PLATFORM_NAMES.has(value as PlatformName);\n}\n\n/** Validate a platform name and return it for typed callers. */\nexport function assertPlatformName(value: string): PlatformName {\n if (!isPlatformName(value)) throw new Error(`Unsupported platform: ${JSON.stringify(value)}`);\n return value;\n}\n\n/** Validate a raw platform conversation identifier before it enters storage. */\nexport function assertConversationId(value: string): string {\n if (value.length === 0 || value === \".\" || value === \"..\") {\n throw new Error(\"Conversation id must be non-empty and not a path marker\");\n }\n for (const character of value) {\n const code = character.codePointAt(0) ?? 0;\n if (\n character === \"/\" ||\n character === \"\\\\\" ||\n code === 0 ||\n code <= 0x1f ||\n (code >= 0x7f && code <= 0x9f)\n ) {\n throw new Error(\"Conversation id must not contain path separators or control characters\");\n }\n }\n return value;\n}\n\n/** Derive a readable diagnostic key whose digest remains the identity authority. */\nexport function officeKey(address: OfficeAddress): OfficeKey {\n const normalized = validateOfficeAddress(address);\n const readable = readableConversationId(normalized.conversationId);\n const digest = createHash(\"sha256\")\n .update(`${OFFICE_KEY_DOMAIN}\\0${normalized.platform}\\0${normalized.conversationId}`)\n .digest(\"hex\")\n .slice(0, OFFICE_KEY_DIGEST_LENGTH);\n return `${OFFICE_KEY_VERSION}-${normalized.platform}-${readable}-${digest}` as OfficeKey;\n}\n\n/** Resolve the persistent workspace directory for an office. */\nexport function officeDir(workspaceRoot: string, address: OfficeAddress): string {\n return join(workspaceRoot, officeKey(address));\n}\n\n/** Resolve the host-only state directory for an office. */\nexport function officeStateDir(stateDir: string, address: OfficeAddress): string {\n return join(stateDir, \"conversations\", officeKey(address));\n}\n\n/**\n * The directory segment naming an office under a workspace root — the same\n * segment on the host and inside the sandbox runtime, so mounts stay a plain\n * root swap. This is the office key: platform-scoped and collision-resistant,\n * so two platforms sharing a raw conversation id can never share a directory.\n * Raw ids reach the filesystem only through the office registry's migration\n * journal (ADR 0005).\n */\nexport function officeDirName(address: OfficeAddress): string {\n return officeKey(address);\n}\n\n/** The host working directory for an office (see officeDirName). */\nexport function conversationOfficeDir(workspaceRoot: string, address: OfficeAddress): string {\n return join(workspaceRoot, officeDirName(address));\n}\n\n/** Compare canonical office identities without relying on their readable key. */\nexport function sameOffice(left: OfficeAddress, right: OfficeAddress): boolean {\n const leftAddress = validateOfficeAddress(left);\n const rightAddress = validateOfficeAddress(right);\n return (\n leftAddress.platform === rightAddress.platform &&\n leftAddress.conversationId === rightAddress.conversationId\n );\n}\n\n/** Validate a persisted or externally supplied office key. */\nexport function assertOfficeKey(value: string): OfficeKey {\n const pattern = /^v1-(slack|discord|telegram|github)-[a-z0-9]+(?:-[a-z0-9]+)*-[a-f0-9]{16}$/;\n if (!pattern.test(value)) throw new Error(`Invalid office key: ${JSON.stringify(value)}`);\n return value as OfficeKey;\n}\n\nexport function isOfficeKey(value: unknown): value is OfficeKey {\n return typeof value === \"string\" && patternMatchesOfficeKey(value);\n}\n\nfunction readableConversationId(conversationId: string): string {\n const readable = conversationId\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .slice(0, READABLE_ID_LENGTH)\n .replace(/-+$/g, \"\");\n return readable || \"conversation\";\n}\n\nfunction patternMatchesOfficeKey(value: string): boolean {\n return /^v1-(slack|discord|telegram|github)-[a-z0-9]+(?:-[a-z0-9]+)*-[a-f0-9]{16}$/.test(value);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"office-migration.d.ts","sourceRoot":"","sources":["../src/office-migration.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG/C,OAAO,KAAK,EAAyB,YAAY,EAAE,MAAM,YAAY,CAAC;AAQtE,MAAM,WAAW,yBAAyB;IACxC,yEAAyE;IACzE,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,uEAAuE;IACvE,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,wEAAwE;IACxE,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,iEAAiE;IACjE,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,yEAAyE;IACzE,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,gFAAgF;IAChF,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,wEAAwE;IACxE,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,gFAAgF;IAChF,iBAAiB,EAAE,MAAM,EAAE,CAAC;CAC7B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE;IAC5C,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,SAAS,YAAY,EAAE,CAAC;CAC3C,GAAG,yBAAyB,CAqC5B;AA4JD,+EAA+E;AAC/E,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,yBAAyB,GAAG,MAAM,CAsCvF;AAkBD,wBAAgB,4BAA4B,CAAC,OAAO,EAAE;IACpD,OAAO,EAAE,SAAS,YAAY,EAAE,CAAC;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CA2B/B"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"office-migration.js","sourceRoot":"","sources":["../src/office-migration.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, lstatSync, readdirSync, renameSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport * as log from \"./log.js\";\nimport { isOfficeKey, officeKey, officeStateDir } from \"./office-address.js\";\nimport { legacyConversationCredentialKey } from \"./sandbox/identity.js\";\nimport type { OfficeRecord } from \"./types.js\";\nimport { OfficeRegistry } from \"./office-registry.js\";\nimport { migrateConversationVaultKeys } from \"./vault/index.js\";\nimport type { OfficeMigrationRecord, PlatformName } from \"./types.js\";\n\n/**\n * Workspace-root entries that are shared infrastructure, never office dirs.\n * Mirrors the boot-time directory setup in main.ts.\n */\nconst RESERVED_WORKSPACE_ENTRIES = new Set([\"skills\", \"events\", \"agents\"]);\n\nexport interface OfficeMigrationRunSummary {\n /** Raw ids whose directories moved to the office-key layout this run. */\n migrated: string[];\n /** Raw ids whose interrupted moves were completed from the journal. */\n recovered: string[];\n /** Raw ids that still need an explicit owner (`mikan office claim`). */\n unowned: string[];\n /** Raw ids whose records are failed and need operator repair. */\n failed: string[];\n /** Raw ids whose conversation vault directories moved to office keys. */\n vaultKeysMigrated: string[];\n /** Raw ids whose legacy and office-key vault dirs both exist (manual merge). */\n vaultConflicts: string[];\n /** Raw ids whose host state dirs (settings, extensions, data) moved. */\n stateDirsMigrated: string[];\n /** Raw ids whose legacy and office-key state dirs both exist (manual merge). */\n stateDirConflicts: string[];\n}\n\n/**\n * Move every legacy raw-id office directory under the workspace root to the\n * canonical office-key layout, journaling each move through the office\n * registry so an interrupted run resumes instead of losing offices.\n *\n * The engine never guesses: with several enabled platforms an unowned raw\n * directory stays in place and is reported for `mikan office claim`. Callers\n * must treat a non-empty `unowned`/`failed` as fatal for daemon boot — after\n * the layout flip, an unmigrated legacy directory is invisible to the\n * runtime, which would silently present the conversation as empty.\n */\nexport function migrateLegacyOffices(options: {\n workspaceRoot: string;\n stateDir: string;\n enabledPlatforms: readonly PlatformName[];\n}): OfficeMigrationRunSummary {\n const registry = new OfficeRegistry(options.stateDir);\n for (const platform of options.enabledPlatforms) registry.enablePlatform(platform);\n\n const summary: OfficeMigrationRunSummary = {\n migrated: [],\n recovered: [],\n unowned: [],\n failed: [],\n vaultKeysMigrated: [],\n vaultConflicts: [],\n stateDirsMigrated: [],\n stateDirConflicts: [],\n };\n\n recoverInterruptedMoves(registry, summary);\n claimAndMoveLegacyDirs(registry, options.workspaceRoot, summary);\n\n // Credential vaults are keyed by office too; the registry inventory (which\n // the directory moves above just extended) drives the same rename.\n const vaults = migrateConversationVaultKeys({\n stateDir: options.stateDir,\n offices: registry.getOffices(),\n });\n summary.vaultKeysMigrated = vaults.migrated;\n summary.vaultConflicts = vaults.conflicts;\n for (const rawConversationId of vaults.migrated) {\n log.logInfo(`[office] Migrated vault key to office key: ${rawConversationId}`);\n }\n\n migrateConversationStateDirs(registry, options.stateDir, summary);\n\n for (const record of registry.getState().migrations) {\n if (record.status === \"needs-owner\") summary.unowned.push(record.rawConversationId);\n else if (record.status === \"failed\") summary.failed.push(record.rawConversationId);\n }\n return summary;\n}\n\n/** Complete or fail every move the journal says was interrupted mid-rename. */\nfunction recoverInterruptedMoves(\n registry: OfficeRegistry,\n summary: OfficeMigrationRunSummary,\n): void {\n for (const record of registry.getState().migrations) {\n if (record.status !== \"moving\") continue;\n const targetDir = record.targetDir;\n if (!targetDir) {\n registry.markFailed(record.rawConversationId, \"Moving record has no target directory\");\n continue;\n }\n const sourceExists = pathExists(record.sourceDir);\n const targetExists = pathExists(targetDir);\n if (sourceExists && targetExists) {\n registry.markFailed(\n record.rawConversationId,\n \"Both legacy source and office target exist; merge them manually\",\n );\n continue;\n }\n if (!sourceExists && !targetExists) {\n registry.markFailed(\n record.rawConversationId,\n \"Neither legacy source nor office target exists\",\n );\n continue;\n }\n if (sourceExists) renameSync(record.sourceDir, targetDir);\n registry.markCommitted(record.rawConversationId);\n recordMigratedOffice(registry, record);\n summary.recovered.push(record.rawConversationId);\n log.logInfo(`[office] Recovered interrupted migration: ${record.rawConversationId}`);\n }\n}\n\n/** Scan the workspace root and move every claimable legacy office directory. */\nfunction claimAndMoveLegacyDirs(\n registry: OfficeRegistry,\n workspaceRoot: string,\n summary: OfficeMigrationRunSummary,\n): void {\n for (const rawConversationId of listLegacyOfficeDirs(workspaceRoot)) {\n const existing = registry.getMigration(rawConversationId);\n if (existing?.status === \"committed\") {\n // The move already happened; a raw-id dir with this name reappearing\n // afterwards is a different (or resurrected) office the runtime can no\n // longer see. Refusing to boot beats silently ignoring its data.\n throw new Error(\n `Legacy office directory reappeared after migration: ${join(workspaceRoot, rawConversationId)}`,\n );\n }\n if (existing?.status === \"failed\") continue; // reported via summary.failed\n\n const record = registry.prepareLegacyMigration({\n rawConversationId,\n sourceDir: join(workspaceRoot, rawConversationId),\n workspaceRoot,\n });\n if (record.status === \"needs-owner\") continue; // reported via summary.unowned\n\n const moving = registry.markMoving(rawConversationId);\n const targetDir = moving.targetDir;\n if (!targetDir) throw new Error(\"Moving office migration has no target directory\");\n renameSync(moving.sourceDir, targetDir);\n registry.markCommitted(rawConversationId);\n recordMigratedOffice(registry, moving);\n summary.migrated.push(rawConversationId);\n log.logInfo(`[office] Migrated office directory: ${rawConversationId} -> ${targetDir}`);\n }\n}\n\n/**\n * Move each registered office's host state tree — settings.json, extensions,\n * extension-data, git checkouts — from `conversations/<rawId>` to\n * `conversations/<officeKey>` in one rename. Conflicts are reported for\n * manual merge, never clobbered.\n */\nfunction migrateConversationStateDirs(\n registry: OfficeRegistry,\n stateDir: string,\n summary: OfficeMigrationRunSummary,\n): void {\n for (const office of registry.getOffices()) {\n const legacyDir = join(stateDir, \"conversations\", office.conversationId);\n if (!existsSync(legacyDir)) continue;\n const targetDir = officeStateDir(stateDir, office);\n if (existsSync(targetDir)) {\n summary.stateDirConflicts.push(office.conversationId);\n continue;\n }\n renameSync(legacyDir, targetDir);\n summary.stateDirsMigrated.push(office.conversationId);\n log.logInfo(`[office] Migrated host state dir to office key: ${office.conversationId}`);\n }\n}\n\n/** A migrated office is an existing office; keep the directory enumerable. */\nfunction recordMigratedOffice(registry: OfficeRegistry, record: OfficeMigrationRecord): void {\n if (!record.ownerPlatform) return;\n registry.recordOffice({\n platform: record.ownerPlatform,\n conversationId: record.rawConversationId,\n });\n}\n\n/**\n * Legacy office candidates: regular directories that are not reserved\n * infrastructure, not hidden, and not already office-key named. A symlink in\n * office position is refused outright — following it could move data from\n * outside the workspace root.\n *\n * A candidate must also look like a conversation office: every office that\n * ever saw a message has a `log.jsonl`, and every office that ran the agent\n * has `sessions/`. Trusted workspace roots legitimately accumulate other\n * directories — repos the agent cloned, build output — which are not offices\n * and must be neither renamed nor reported as unowned; they stay where they\n * are (an operator can still claim one explicitly via `mikan office claim`).\n */\nfunction listLegacyOfficeDirs(workspaceRoot: string): string[] {\n const candidates: string[] = [];\n for (const entry of readdirSync(workspaceRoot, { withFileTypes: true })) {\n if (entry.name.startsWith(\".\")) continue;\n if (RESERVED_WORKSPACE_ENTRIES.has(entry.name)) continue;\n if (isOfficeKey(entry.name)) continue;\n if (entry.isSymbolicLink()) {\n throw new Error(`Workspace entry must not be a symlink: ${join(workspaceRoot, entry.name)}`);\n }\n if (!entry.isDirectory()) continue;\n if (!looksLikeConversationOffice(join(workspaceRoot, entry.name))) {\n log.logInfo(\n `[office] Skipping non-office workspace directory (no log.jsonl or sessions/): ${entry.name}`,\n );\n continue;\n }\n candidates.push(entry.name);\n }\n return candidates.toSorted();\n}\n\nfunction looksLikeConversationOffice(dir: string): boolean {\n return existsSync(join(dir, \"log.jsonl\")) || existsSync(join(dir, \"sessions\"));\n}\n\nfunction pathExists(path: string): boolean {\n try {\n lstatSync(path);\n return true;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return false;\n throw err;\n }\n}\n\n/** Operator-facing boot failure for offices the engine may not move itself. */\nexport function formatUnmigratedOfficesError(summary: OfficeMigrationRunSummary): string {\n const lines = [\"Conversation office migration cannot complete:\"];\n if (summary.unowned.length > 0) {\n lines.push(\n \"\",\n \"These legacy conversation directories have no owning platform (several\",\n \"platforms are enabled, so ownership cannot be inferred):\",\n ...summary.unowned.map((id) => ` - ${id}`),\n \"\",\n \"Assign each one with:\",\n \" mikan office claim <conversationId> <platform>\",\n );\n }\n if (summary.failed.length > 0) {\n lines.push(\n \"\",\n \"These migrations previously failed and need manual repair (see\",\n \"office-registry.json in the state dir for each error):\",\n ...summary.failed.map((id) => ` - ${id}`),\n );\n }\n if (summary.vaultConflicts.length > 0) {\n lines.push(\n \"\",\n \"These conversations have credentials under both the legacy and the\",\n \"office-key vault directory; merge them manually under <state-dir>/vaults:\",\n ...summary.vaultConflicts.map((id) => ` - ${id}`),\n );\n }\n if (summary.stateDirConflicts.length > 0) {\n lines.push(\n \"\",\n \"These conversations have host state under both the legacy and the\",\n \"office-key directory; merge them manually under <state-dir>/conversations:\",\n ...summary.stateDirConflicts.map((id) => ` - ${id}`),\n );\n }\n return lines.join(\"\\n\");\n}\n\n/**\n * Bind-spec translator for the container layout migration: rewrites every\n * host path the office migration renamed — workspace dirs, per-conversation\n * state trees, conversation vaults — and the guest workspace segment, using\n * the registry's office inventory as the raw-id ↔ office mapping. Specs that\n * reference none of the renamed paths come back unchanged, which is also how\n * an already-migrated container is recognized.\n */\nfunction replacePrefix(path: string, pairs: Array<[string, string]>): string {\n for (const [oldPrefix, newPrefix] of pairs) {\n if (path === oldPrefix) return newPrefix;\n if (path.startsWith(`${oldPrefix}/`)) return newPrefix + path.slice(oldPrefix.length);\n }\n return path;\n}\n\nexport function buildContainerBindTranslator(options: {\n offices: readonly OfficeRecord[];\n workspaceRoot: string;\n stateDir: string;\n}): (bindSpec: string) => string {\n const hostPairs: Array<[string, string]> = [];\n const guestPairs: Array<[string, string]> = [];\n for (const office of options.offices) {\n const key = officeKey(office);\n const rawId = office.conversationId;\n hostPairs.push([join(options.workspaceRoot, rawId), join(options.workspaceRoot, key)]);\n hostPairs.push([\n join(options.stateDir, \"conversations\", rawId),\n officeStateDir(options.stateDir, office),\n ]);\n hostPairs.push([\n join(options.stateDir, \"vaults\", legacyConversationCredentialKey(rawId)),\n join(options.stateDir, \"vaults\", key),\n ]);\n guestPairs.push([`/workspace/${rawId}`, `/workspace/${key}`]);\n }\n\n return (bindSpec: string): string => {\n const readOnly = bindSpec.endsWith(\":ro\");\n const spec = readOnly ? bindSpec.slice(0, -3) : bindSpec;\n const separator = spec.indexOf(\":\");\n if (separator === -1) return bindSpec;\n const source = replacePrefix(spec.slice(0, separator), hostPairs);\n const target = replacePrefix(spec.slice(separator + 1), guestPairs);\n return `${source}:${target}${readOnly ? \":ro\" : \"\"}`;\n };\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"office-registry.d.ts","sourceRoot":"","sources":["../src/office-registry.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,aAAa,EACb,0BAA0B,EAC1B,qBAAqB,EAErB,YAAY,EACZ,mBAAmB,EACnB,YAAY,EACb,MAAM,YAAY,CAAC;AA4BpB;;;;;;;;;GASG;AACH,UAAU,qBAAqB;IAC7B,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACrD,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA0C;IACrE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,KAAK,CAAsB;IAEnC,YAAY,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,qBAA0B,EAShE;IAED,6EAA6E;IAC7E,MAAM,IAAI,mBAAmB,CAG5B;IAED,QAAQ,IAAI,mBAAmB,CAE9B;IAED,YAAY,CAAC,iBAAiB,EAAE,MAAM,GAAG,qBAAqB,GAAG,SAAS,CAGzE;IAED,cAAc,CAAC,QAAQ,EAAE,YAAY,GAAG,mBAAmB,CAS1D;IAED;;;;OAIG;IACH,YAAY,CAAC,OAAO,EAAE,aAAa,GAAG,YAAY,CAmBjD;IAED,UAAU,IAAI,SAAS,YAAY,EAAE,CAEpC;IAED,OAAO,CAAC,UAAU;IAOlB;;;;OAIG;IACH,sBAAsB,CAAC,OAAO,EAAE,0BAA0B,GAAG,qBAAqB,CAsDjF;IAED,UAAU,CAAC,iBAAiB,EAAE,MAAM,GAAG,qBAAqB,CAyB3D;IAED,aAAa,CAAC,iBAAiB,EAAE,MAAM,GAAG,qBAAqB,CAgB9D;IAED,UAAU,CAAC,iBAAiB,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,qBAAqB,CAS1E;IAED,OAAO,CAAC,YAAY;IAkBpB,OAAO,CAAC,gBAAgB;IAOxB,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,gBAAgB;IAaxB,OAAO,CAAC,YAAY;IAUpB;;;;OAIG;IACH,OAAO,CAAC,kBAAkB;IAU1B,OAAO,CAAC,SAAS;CAelB;AAgXD;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,MAAM,CAerF;AAED;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CACvC,iBAAiB,EAAE,MAAM,EACzB,QAAQ,GAAE,MAA4B,GACrC,aAAa,CAkCf;AAED,+EAA+E;AAC/E,wBAAgB,qBAAqB,IAAI,SAAS,YAAY,EAAE,CAI/D"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"office-registry.js","sourceRoot":"","sources":["../src/office-registry.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomBytes } from \"node:crypto\";\nimport { lstatSync, mkdirSync, readFileSync, rmSync, statSync, writeFileSync } from \"node:fs\";\nimport { isAbsolute, join, resolve } from \"node:path\";\nimport type {\n OfficeAddress,\n OfficeMigrationPreparation,\n OfficeMigrationRecord,\n OfficeMigrationStatus,\n OfficeRecord,\n OfficeRegistryState,\n PlatformName,\n} from \"./types.js\";\nimport {\n officeDir,\n officeKey,\n assertConversationId,\n assertPlatformName,\n conversationOfficeDir,\n validateOfficeAddress,\n} from \"./office-address.js\";\nimport { parseGithubConversationId } from \"./adapters/github/ids.js\";\nimport { effectiveStateDir } from \"./cli/arg-grammar.js\";\nimport { isRecord, readTextFileIfExists } from \"./utils/file-guards.js\";\nimport { atomicWritePrivateFile } from \"./utils/fs-atomic.js\";\n\nconst REGISTRY_VERSION = 1;\nconst REGISTRY_FILENAME = \"office-registry.json\";\nconst MIGRATION_STATUSES = new Set<OfficeMigrationStatus>([\n \"needs-owner\",\n \"prepared\",\n \"moving\",\n \"committed\",\n \"failed\",\n]);\nconst REGISTRY_LOCK_FILENAME = \".office-registry.lock\";\nconst REGISTRY_LOCK_RETRY_MS = 25;\nconst REGISTRY_LOCK_TIMEOUT_MS = 5_000;\nconst REGISTRY_LOCK_STALE_MS = 60_000;\n\n/**\n * Host-only office directory and migration journal.\n *\n * Office records are the durable raw-id ↔ (platform, office) mapping: once\n * the ADR 0005 layout migration renames workspace dirs to office keys, dir\n * names stop carrying raw platform ids, and enumeration/legacy-scope lookups\n * resolve through these records. Migration records track claiming legacy\n * raw-id directories; existing runtime consumers keep their current layout\n * until that migration commit lands.\n */\ninterface OfficeRegistryOptions {\n writeState?: (path: string, content: string) => void;\n lockTimeoutMs?: number;\n}\n\nexport class OfficeRegistry {\n private readonly stateDir: string;\n private readonly registryPath: string;\n private readonly lockPath: string;\n private readonly writeState: (path: string, content: string) => void;\n private readonly lockTimeoutMs: number;\n private state: OfficeRegistryState;\n\n constructor(stateDir: string, options: OfficeRegistryOptions = {}) {\n this.stateDir = resolve(stateDir);\n mkdirSync(this.stateDir, { recursive: true, mode: 0o700 });\n assertRegularDirectory(this.stateDir, \"State directory\");\n this.registryPath = resolve(this.stateDir, REGISTRY_FILENAME);\n this.lockPath = join(this.stateDir, REGISTRY_LOCK_FILENAME);\n this.writeState = options.writeState ?? atomicWritePrivateFile;\n this.lockTimeoutMs = options.lockTimeoutMs ?? REGISTRY_LOCK_TIMEOUT_MS;\n this.state = this.readState();\n }\n\n /** Reload the atomic registry file after another host process changed it. */\n reload(): OfficeRegistryState {\n this.state = this.readState();\n return this.state;\n }\n\n getState(): OfficeRegistryState {\n return this.state;\n }\n\n getMigration(rawConversationId: string): OfficeMigrationRecord | undefined {\n assertConversationId(rawConversationId);\n return this.state.migrations.find((record) => record.rawConversationId === rawConversationId);\n }\n\n enablePlatform(platform: PlatformName): OfficeRegistryState {\n const validPlatform = assertPlatformName(platform);\n return this.withExclusiveLease(() => {\n if (this.state.enabledPlatforms.includes(validPlatform)) return this.state;\n\n const enabledPlatforms = [...this.state.enabledPlatforms, validPlatform].toSorted();\n this.replaceState(enabledPlatforms, this.state.offices, this.state.migrations);\n return this.state;\n });\n }\n\n /**\n * Record that an office exists. Idempotent: re-recording a known office is\n * a lock-free cache hit for callers that materialize directories per\n * message, so this sits safely on hot write paths.\n */\n recordOffice(address: OfficeAddress): OfficeRecord {\n const normalized = validateOfficeAddress(address);\n const existing = this.findOffice(normalized);\n if (existing) return existing;\n return this.withExclusiveLease(() => {\n const found = this.findOffice(normalized);\n if (found) return found;\n const record: OfficeRecord = Object.freeze({\n platform: normalized.platform,\n conversationId: normalized.conversationId,\n recordedAt: new Date().toISOString(),\n });\n this.replaceState(\n this.state.enabledPlatforms,\n [...this.state.offices, record],\n this.state.migrations,\n );\n return record;\n });\n }\n\n getOffices(): readonly OfficeRecord[] {\n return this.state.offices;\n }\n\n private findOffice(address: OfficeAddress): OfficeRecord | undefined {\n return this.state.offices.find(\n (record) =>\n record.platform === address.platform && record.conversationId === address.conversationId,\n );\n }\n\n /**\n * Validate and record a legacy directory. With exactly one enabled platform,\n * ownership is safe to infer. With zero or multiple platforms, the record\n * remains explicit `needs-owner` unless the caller supplies an owner.\n */\n prepareLegacyMigration(options: OfficeMigrationPreparation): OfficeMigrationRecord {\n const rawConversationId = assertConversationId(options.rawConversationId);\n const sourceDir = resolve(options.sourceDir);\n const workspaceRoot = resolve(options.workspaceRoot);\n return this.withExclusiveLease(() => {\n const existing = this.getMigration(rawConversationId);\n assertCanonicalSource(sourceDir, workspaceRoot, rawConversationId);\n if (existing) {\n assertSameMigrationInputs(existing, sourceDir, workspaceRoot);\n if (existing.status !== \"needs-owner\") return existing;\n }\n\n assertRegularDirectory(sourceDir, \"Legacy office source\");\n const ownerPlatform = this.resolveOwner(options.ownerPlatform, rawConversationId);\n if (!ownerPlatform) {\n const record = makeRecord({\n rawConversationId,\n sourceDir,\n workspaceRoot,\n status: \"needs-owner\",\n });\n this.replaceMigration(existing, record);\n return record;\n }\n\n const targetDir = officeDir(workspaceRoot, {\n platform: ownerPlatform,\n conversationId: rawConversationId,\n });\n if (pathExists(targetDir)) {\n const failed = makeRecord({\n rawConversationId,\n sourceDir,\n workspaceRoot,\n ownerPlatform,\n targetDir,\n status: \"failed\",\n error: \"Target office directory already exists\",\n });\n this.replaceMigration(existing, failed);\n throw new Error(`${failed.error}: ${targetDir}`);\n }\n\n const prepared = makeRecord({\n rawConversationId,\n sourceDir,\n workspaceRoot,\n ownerPlatform,\n targetDir,\n status: \"prepared\",\n });\n this.replaceMigration(existing, prepared);\n return prepared;\n });\n }\n\n markMoving(rawConversationId: string): OfficeMigrationRecord {\n return this.withExclusiveLease(() => {\n const record = this.requireMigration(rawConversationId);\n if (\n record.status === \"moving\" ||\n record.status === \"committed\" ||\n record.status === \"failed\"\n ) {\n return record;\n }\n if (record.status === \"needs-owner\") {\n throw new Error(`Legacy office ${JSON.stringify(rawConversationId)} still needs an owner`);\n }\n assertRegularDirectory(record.sourceDir, \"Legacy office source\");\n const targetDir = migrationTarget(record);\n if (!targetDir) throw new Error(\"Prepared office migration has no target directory\");\n if (pathExists(targetDir)) {\n const failed = this.failRecord(record, \"Target office directory appeared before moving\");\n throw new Error(`${failed.error}: ${targetDir}`);\n }\n\n const moving = transitionRecord(record, \"moving\");\n this.replaceMigration(record, moving);\n return moving;\n });\n }\n\n markCommitted(rawConversationId: string): OfficeMigrationRecord {\n return this.withExclusiveLease(() => {\n const record = this.requireMigration(rawConversationId);\n if (record.status === \"committed\" || record.status === \"failed\") return record;\n if (record.status !== \"moving\") {\n throw new Error(`Cannot commit office migration from ${record.status}`);\n }\n const targetDir = migrationTarget(record);\n if (!targetDir) throw new Error(\"Moving office migration has no target directory\");\n assertRegularDirectory(targetDir, \"Migrated office target\");\n assertPathAbsent(record.sourceDir, \"Legacy office source\");\n\n const committed = transitionRecord(record, \"committed\");\n this.replaceMigration(record, committed);\n return committed;\n });\n }\n\n markFailed(rawConversationId: string, error: string): OfficeMigrationRecord {\n return this.withExclusiveLease(() => {\n const record = this.requireMigration(rawConversationId);\n if (record.status === \"committed\") {\n throw new Error(\"A committed office migration cannot be marked failed\");\n }\n if (record.status === \"failed\") return record;\n return this.failRecord(record, error);\n });\n }\n\n private resolveOwner(\n ownerPlatform: PlatformName | undefined,\n rawConversationId: string,\n ): PlatformName | undefined {\n if (ownerPlatform !== undefined) {\n const validPlatform = assertPlatformName(ownerPlatform);\n if (!this.state.enabledPlatforms.includes(validPlatform)) {\n throw new Error(`Owner platform ${validPlatform} is not enabled`);\n }\n return validPlatform;\n }\n const candidates = platformsMatchingConversationIdFormat(\n rawConversationId,\n this.state.enabledPlatforms,\n );\n return candidates.length === 1 ? candidates[0] : undefined;\n }\n\n private requireMigration(rawConversationId: string): OfficeMigrationRecord {\n assertConversationId(rawConversationId);\n const record = this.getMigration(rawConversationId);\n if (!record) throw new Error(`No office migration registered for ${rawConversationId}`);\n return record;\n }\n\n private failRecord(record: OfficeMigrationRecord, error: string): OfficeMigrationRecord {\n const message = error.trim();\n if (!message) throw new Error(\"Office migration failure reason must not be empty\");\n const failed = transitionRecord(record, \"failed\", message);\n this.replaceMigration(record, failed);\n return failed;\n }\n\n private replaceMigration(\n previous: OfficeMigrationRecord | undefined,\n next: OfficeMigrationRecord,\n ): void {\n const migrations = [...this.state.migrations];\n const index = previous\n ? migrations.findIndex((record) => record.rawConversationId === previous.rawConversationId)\n : -1;\n if (index === -1) migrations.push(next);\n else migrations[index] = next;\n this.replaceState(this.state.enabledPlatforms, this.state.offices, migrations);\n }\n\n private replaceState(\n enabledPlatforms: readonly PlatformName[],\n offices: readonly OfficeRecord[],\n migrations: readonly OfficeMigrationRecord[],\n ): void {\n const candidate = freezeState({ enabledPlatforms, offices, migrations });\n this.writeState(this.registryPath, `${JSON.stringify(candidate, null, 2)}\\n`);\n this.state = candidate;\n }\n\n /**\n * Acquire the domain lease, reload while holding it, then perform one\n * read-modify-write. A future move transaction can retain the same lease\n * across its filesystem operation instead of splitting the transition.\n */\n private withExclusiveLease<T>(operation: () => T): T {\n const release = acquireRegistryLease(this.lockPath, this.lockTimeoutMs);\n try {\n this.state = this.readState();\n return operation();\n } finally {\n release();\n }\n }\n\n private readState(): OfficeRegistryState {\n assertRegistryFileSafe(this.registryPath);\n const raw = readTextFileIfExists(this.registryPath);\n if (raw === undefined) {\n return freezeState({ enabledPlatforms: [], offices: [], migrations: [] });\n }\n\n let value: unknown;\n try {\n value = JSON.parse(raw);\n } catch (error) {\n throw new Error(`Invalid office registry JSON at ${this.registryPath}`, { cause: error });\n }\n return parseState(value, this.registryPath);\n }\n}\n\ninterface RecordFields {\n rawConversationId: string;\n sourceDir: string;\n workspaceRoot: string;\n ownerPlatform?: PlatformName;\n targetDir?: string;\n status: OfficeMigrationStatus;\n error?: string;\n updatedAt?: string;\n}\n\nfunction makeRecord(fields: RecordFields): OfficeMigrationRecord {\n const record: OfficeMigrationRecord = {\n rawConversationId: fields.rawConversationId,\n sourceDir: fields.sourceDir,\n workspaceRoot: fields.workspaceRoot,\n ...(fields.ownerPlatform ? { ownerPlatform: fields.ownerPlatform } : {}),\n ...(fields.targetDir ? { targetDir: fields.targetDir } : {}),\n status: fields.status,\n ...(fields.error ? { error: fields.error } : {}),\n updatedAt: fields.updatedAt ?? new Date().toISOString(),\n };\n return Object.freeze(record);\n}\n\nfunction transitionRecord(\n record: OfficeMigrationRecord,\n status: OfficeMigrationStatus,\n error?: string,\n): OfficeMigrationRecord {\n const targetDir = migrationTarget(record);\n return makeRecord({\n rawConversationId: record.rawConversationId,\n sourceDir: record.sourceDir,\n workspaceRoot: record.workspaceRoot,\n ...(record.ownerPlatform ? { ownerPlatform: record.ownerPlatform } : {}),\n ...(targetDir ? { targetDir } : {}),\n status,\n ...(error ? { error } : {}),\n });\n}\n\nfunction migrationTarget(record: OfficeMigrationRecord): string | undefined {\n return record.ownerPlatform\n ? officeDir(record.workspaceRoot, {\n platform: record.ownerPlatform,\n conversationId: record.rawConversationId,\n })\n : undefined;\n}\n\nfunction freezeState(input: {\n enabledPlatforms: readonly PlatformName[];\n offices: readonly OfficeRecord[];\n migrations: readonly OfficeMigrationRecord[];\n}): OfficeRegistryState {\n return Object.freeze({\n version: REGISTRY_VERSION as 1,\n enabledPlatforms: Object.freeze([...input.enabledPlatforms]),\n offices: Object.freeze(input.offices.map((record) => Object.freeze({ ...record }))),\n migrations: Object.freeze(input.migrations.map((record) => Object.freeze({ ...record }))),\n });\n}\n\nfunction parseState(value: unknown, path: string): OfficeRegistryState {\n if (!isRecord(value) || value.version !== REGISTRY_VERSION) {\n throw new Error(`Invalid office registry version at ${path}`);\n }\n if (!Array.isArray(value.enabledPlatforms) || !Array.isArray(value.migrations)) {\n throw new Error(`Invalid office registry shape at ${path}`);\n }\n\n const enabledPlatforms = value.enabledPlatforms.map((platform) => {\n if (typeof platform !== \"string\") throw new Error(`Invalid enabled platform in ${path}`);\n return assertPlatformName(platform);\n });\n if (new Set(enabledPlatforms).size !== enabledPlatforms.length) {\n throw new Error(`Duplicate enabled platform in ${path}`);\n }\n\n // Files written before office records existed simply have no offices yet.\n const offices = (Array.isArray(value.offices) ? value.offices : []).map((entry) =>\n parseOfficeRecord(entry, path),\n );\n if (new Set(offices.map((record) => officeKey(record))).size !== offices.length) {\n throw new Error(`Duplicate office record in ${path}`);\n }\n\n const migrations = value.migrations.map((entry) => parseRecord(entry, path, enabledPlatforms));\n if (new Set(migrations.map((record) => record.rawConversationId)).size !== migrations.length) {\n throw new Error(`Duplicate office migration in ${path}`);\n }\n return freezeState({ enabledPlatforms, offices, migrations });\n}\n\nfunction parseOfficeRecord(value: unknown, path: string): OfficeRecord {\n if (!isRecord(value)) throw new Error(`Invalid office record in ${path}`);\n if (\n typeof value.platform !== \"string\" ||\n typeof value.conversationId !== \"string\" ||\n typeof value.recordedAt !== \"string\"\n ) {\n throw new Error(`Invalid office record fields in ${path}`);\n }\n return Object.freeze({\n platform: assertPlatformName(value.platform),\n conversationId: assertConversationId(value.conversationId),\n recordedAt: value.recordedAt,\n });\n}\n\nfunction parseRecord(\n value: unknown,\n path: string,\n enabledPlatforms: readonly PlatformName[],\n): OfficeMigrationRecord {\n if (!isRecord(value)) throw new Error(`Invalid office migration record in ${path}`);\n if (\n typeof value.rawConversationId !== \"string\" ||\n typeof value.sourceDir !== \"string\" ||\n typeof value.workspaceRoot !== \"string\" ||\n typeof value.status !== \"string\" ||\n typeof value.updatedAt !== \"string\"\n ) {\n throw new Error(`Invalid office migration fields in ${path}`);\n }\n assertConversationId(value.rawConversationId);\n if (!isAbsolute(value.sourceDir) || !isAbsolute(value.workspaceRoot)) {\n throw new Error(`Office migration paths must be absolute in ${path}`);\n }\n assertCanonicalSource(value.sourceDir, value.workspaceRoot, value.rawConversationId);\n if (!MIGRATION_STATUSES.has(value.status as OfficeMigrationStatus)) {\n throw new Error(`Invalid office migration status in ${path}`);\n }\n\n const ownerPlatform = optionalPlatform(value.ownerPlatform);\n const targetDir = optionalString(value.targetDir);\n const error = optionalString(value.error);\n if (ownerPlatform !== undefined && !enabledPlatforms.includes(ownerPlatform)) {\n throw new Error(`Office migration owner is not enabled in ${path}`);\n }\n if (value.status === \"needs-owner\" && (ownerPlatform !== undefined || targetDir !== undefined)) {\n throw new Error(`Needs-owner migration cannot have an owner or target in ${path}`);\n }\n if (\n value.status !== \"needs-owner\" &&\n value.status !== \"failed\" &&\n (ownerPlatform === undefined || targetDir === undefined)\n ) {\n throw new Error(`Prepared office migration is missing ownership in ${path}`);\n }\n if (ownerPlatform === undefined && targetDir !== undefined) {\n throw new Error(`Office migration target has no owner in ${path}`);\n }\n const expectedTargetDir = ownerPlatform\n ? officeDir(value.workspaceRoot, {\n platform: ownerPlatform,\n conversationId: value.rawConversationId,\n })\n : undefined;\n if (targetDir !== expectedTargetDir) {\n throw new Error(`Office migration target does not match its office address in ${path}`);\n }\n if (value.status === \"failed\" && !error) {\n throw new Error(`Failed office migration is missing an error in ${path}`);\n }\n\n return makeRecord({\n rawConversationId: value.rawConversationId,\n sourceDir: value.sourceDir,\n workspaceRoot: value.workspaceRoot,\n ...(ownerPlatform ? { ownerPlatform } : {}),\n ...(expectedTargetDir ? { targetDir: expectedTargetDir } : {}),\n status: value.status as OfficeMigrationStatus,\n ...(error ? { error } : {}),\n updatedAt: value.updatedAt,\n });\n}\n\nfunction optionalPlatform(value: unknown): PlatformName | undefined {\n return value === undefined\n ? undefined\n : typeof value === \"string\"\n ? assertPlatformName(value)\n : failOptional(\"platform\");\n}\n\nfunction optionalString(value: unknown): string | undefined {\n return value === undefined\n ? undefined\n : typeof value === \"string\"\n ? value\n : failOptional(\"string\");\n}\n\nfunction failOptional(label: string): never {\n throw new Error(`Invalid optional ${label} in office registry`);\n}\n\nfunction assertSameMigrationInputs(\n existing: OfficeMigrationRecord,\n sourceDir: string,\n workspaceRoot: string,\n): void {\n if (existing.sourceDir !== sourceDir || existing.workspaceRoot !== workspaceRoot) {\n throw new Error(`Legacy office migration inputs changed for ${existing.rawConversationId}`);\n }\n}\n\nfunction assertCanonicalSource(\n sourceDir: string,\n workspaceRoot: string,\n rawConversationId: string,\n): void {\n const expectedSourceDir = resolve(workspaceRoot, rawConversationId);\n if (sourceDir !== expectedSourceDir) {\n throw new Error(`Legacy office source must be ${expectedSourceDir}`);\n }\n assertRegularDirectory(workspaceRoot, \"Workspace root\");\n assertPathIfPresent(sourceDir, \"Legacy office source\");\n}\n\nfunction assertRegularDirectory(path: string, label: string): void {\n let stat;\n try {\n stat = lstatSync(path);\n } catch (error) {\n throw new Error(`${label} does not exist: ${path}`, { cause: error });\n }\n if (stat.isSymbolicLink() || !stat.isDirectory()) {\n throw new Error(`${label} must be a regular directory: ${path}`);\n }\n}\n\nfunction assertRegistryFileSafe(path: string): void {\n try {\n const stat = lstatSync(path);\n if (stat.isSymbolicLink() || !stat.isFile()) {\n throw new Error(`Office registry must be a regular file: ${path}`);\n }\n } catch (error) {\n if (isErrno(error, \"ENOENT\")) return;\n throw error;\n }\n}\n\nfunction assertPathIfPresent(path: string, label: string): void {\n try {\n const stat = lstatSync(path);\n if (stat.isSymbolicLink() || !stat.isDirectory()) {\n throw new Error(`${label} must be a regular directory: ${path}`);\n }\n } catch (error) {\n if (isErrno(error, \"ENOENT\")) return;\n throw error;\n }\n}\n\nfunction assertPathAbsent(path: string, label: string): void {\n try {\n lstatSync(path);\n } catch (error) {\n if (isErrno(error, \"ENOENT\")) return;\n throw error;\n }\n throw new Error(`${label} must be absent: ${path}`);\n}\n\nfunction pathExists(path: string): boolean {\n try {\n lstatSync(path);\n return true;\n } catch (error) {\n if (isErrno(error, \"ENOENT\")) return false;\n throw error;\n }\n}\n\nfunction acquireRegistryLease(lockPath: string, timeoutMs: number): () => void {\n const deadline = Date.now() + timeoutMs;\n const token = `${process.pid}:${randomBytes(8).toString(\"hex\")}`;\n\n for (;;) {\n try {\n mkdirSync(lockPath, { mode: 0o700 });\n try {\n writeFileSync(join(lockPath, \"owner\"), `${token}\\n`, { mode: 0o600 });\n } catch (error) {\n rmSync(lockPath, { recursive: true, force: true });\n throw error;\n }\n return () => releaseRegistryLease(lockPath, token);\n } catch (error) {\n if (!isErrno(error, \"EEXIST\")) throw error;\n if (registryLockIsStale(lockPath)) {\n rmSync(lockPath, { recursive: true, force: true });\n continue;\n }\n if (Date.now() >= deadline) {\n throw new Error(`Timed out acquiring office registry lock: ${lockPath}`, { cause: error });\n }\n sleepSync(REGISTRY_LOCK_RETRY_MS);\n }\n }\n}\n\nfunction releaseRegistryLease(lockPath: string, token: string): void {\n try {\n if (readFileSync(join(lockPath, \"owner\"), \"utf8\").trim() === token) {\n rmSync(lockPath, { recursive: true, force: true });\n }\n } catch (error) {\n if (!isErrno(error, \"ENOENT\")) throw error;\n }\n}\n\nfunction registryLockIsStale(lockPath: string): boolean {\n let ownerKnown = false;\n let ownerAlive = false;\n try {\n const owner = readFileSync(join(lockPath, \"owner\"), \"utf8\").trim();\n const pid = Number(owner.split(\":\", 1)[0]);\n if (Number.isInteger(pid) && pid > 0) {\n ownerKnown = true;\n try {\n process.kill(pid, 0);\n ownerAlive = true;\n } catch (error) {\n if (isErrno(error, \"EPERM\")) ownerAlive = true;\n }\n }\n } catch {\n // The owner file may not have been written before its process died.\n }\n try {\n const oldEnough = Date.now() - statSync(lockPath).mtimeMs >= REGISTRY_LOCK_STALE_MS;\n return ownerKnown ? !ownerAlive : oldEnough;\n } catch {\n return false;\n }\n}\n\nfunction sleepSync(milliseconds: number): void {\n Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, milliseconds);\n}\n\n// ── office materialization ────────────────────────────────────────────────────\n\n/**\n * Process-wide registries keyed by state dir. Office materialization happens\n * on hot per-message paths (log appends, attachment writes), so each state\n * dir keeps one registry instance plus the set of office keys already\n * recorded; after the first sighting an office costs one Set lookup.\n */\nconst registriesByStateDir = new Map<string, OfficeRegistry>();\nconst recordedOfficesByStateDir = new Map<string, Set<string>>();\n\nfunction registryFor(stateDir: string): OfficeRegistry {\n const key = resolve(stateDir);\n const existing = registriesByStateDir.get(key);\n if (existing) return existing;\n const registry = new OfficeRegistry(key);\n registriesByStateDir.set(key, registry);\n return registry;\n}\n\n/**\n * Materialize an office working directory: record the office in the registry,\n * then create the directory. Record-first ordering means a crash can leave a\n * record without a directory (harmless; recreated on the next message) but\n * never an anonymous office directory the registry cannot enumerate.\n *\n * This is the single creation seam for office dirs; read paths keep using\n * `conversationOfficeDir` and simply see missing files for unknown offices.\n */\nexport function ensureOfficeDir(workspaceRoot: string, address: OfficeAddress): string {\n const normalized = validateOfficeAddress(address);\n const stateKey = resolve(effectiveStateDir());\n const cached = recordedOfficesByStateDir.get(stateKey) ?? new Set<string>();\n if (!recordedOfficesByStateDir.has(stateKey)) recordedOfficesByStateDir.set(stateKey, cached);\n\n const key = officeKey(normalized);\n if (!cached.has(key)) {\n registryFor(stateKey).recordOffice(normalized);\n cached.add(key);\n }\n\n const dir = conversationOfficeDir(workspaceRoot, normalized);\n ensureRegularOfficeDirectory(dir);\n return dir;\n}\n\n/**\n * Resolve the office behind a raw conversation id for surfaces that predate\n * OfficeAddress (Admin scope, settings mutation). The registry is the only\n * authority: office records first, then a committed migration's owner, then\n * the sole enabled platform. Two platforms sharing the raw id make the scope\n * genuinely ambiguous, and an unknown id has no office to resolve — both\n * fail loudly rather than guessing a directory.\n */\nexport function resolveOwnedOfficeAddress(\n rawConversationId: string,\n stateDir: string = effectiveStateDir(),\n): OfficeAddress {\n assertConversationId(rawConversationId);\n const registry = registryFor(stateDir);\n\n const owners = () =>\n registry.getState().offices.filter((record) => record.conversationId === rawConversationId);\n let matches = owners();\n if (matches.length === 0) {\n // Another process (the daemon, `mikan office claim`) may have written\n // since this instance loaded; check the file before giving up.\n registry.reload();\n matches = owners();\n }\n if (matches.length === 1) {\n return { platform: matches[0].platform, conversationId: rawConversationId };\n }\n if (matches.length > 1) {\n throw new Error(\n `Conversation id ${JSON.stringify(rawConversationId)} names offices on several platforms; ` +\n \"use a platform-scoped surface instead\",\n );\n }\n\n const migration = registry.getMigration(rawConversationId);\n if (migration?.ownerPlatform) {\n return { platform: migration.ownerPlatform, conversationId: rawConversationId };\n }\n const { enabledPlatforms } = registry.getState();\n if (enabledPlatforms.length === 1) {\n return { platform: enabledPlatforms[0], conversationId: rawConversationId };\n }\n throw new Error(\n `No office is registered for conversation id ${JSON.stringify(rawConversationId)}`,\n );\n}\n\n/** Current office inventory, re-read from disk for cross-process freshness. */\nexport function listRegisteredOffices(): readonly OfficeRecord[] {\n const registry = registryFor(effectiveStateDir());\n registry.reload();\n return registry.getOffices();\n}\n\n/** mkdir-if-missing with the same fail-closed type guard as projection roots. */\nfunction ensureRegularOfficeDirectory(dir: string): void {\n let stats;\n try {\n stats = lstatSync(dir);\n } catch (err) {\n if (!isErrno(err, \"ENOENT\")) throw err;\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n return;\n }\n if (stats.isSymbolicLink() || !stats.isDirectory()) {\n throw new Error(`Office directory must be a regular non-symlink directory: ${dir}`);\n }\n}\n\n/**\n * Enabled platforms whose id format could have produced this raw\n * conversation id. Auto-claiming uses this as verification, not guessing:\n * every format below is one mikan itself writes (GitHub ids are mikan-derived\n * slugs; Slack/Telegram/Discord ids are the platforms' own grammars), and a\n * directory is only claimed when exactly one enabled platform's format\n * matches. Bare digits stay ambiguous while both Telegram and Discord are\n * enabled (negative ids are Telegram-only); anything matching no enabled\n * format fails closed to needs-owner.\n */\nfunction platformsMatchingConversationIdFormat(\n rawConversationId: string,\n enabledPlatforms: readonly PlatformName[],\n): PlatformName[] {\n return enabledPlatforms.filter((platform) => {\n switch (platform) {\n case \"github\":\n return isGithubConversationId(rawConversationId);\n case \"telegram\":\n return /^-?\\d+$/.test(rawConversationId);\n case \"discord\":\n return /^\\d+$/.test(rawConversationId);\n case \"slack\":\n return /^[A-Z][A-Z0-9]*$/.test(rawConversationId);\n }\n });\n}\n\nfunction isGithubConversationId(rawConversationId: string): boolean {\n try {\n parseGithubConversationId(rawConversationId);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction isErrno(error: unknown, code: string): error is NodeJS.ErrnoException {\n return error instanceof Error && \"code\" in error && error.code === code;\n}\n"]}
package/dist/store.d.ts DELETED
@@ -1,51 +0,0 @@
1
- export type { Attachment, ChannelStoreConfig, LoggedMessage } from "./types.js";
2
- import type { Attachment, ChannelStoreConfig, LoggedMessage } from "./types.js";
3
- /**
4
- * Slack office store: message logs and attachment downloads for one Slack
5
- * workspace (the bot token is a Slack credential). Channel ids are therefore
6
- * Slack conversation ids; other platforms log through their adapters.
7
- */
8
- export declare class ChannelStore {
9
- private workingDir;
10
- private botToken;
11
- private recentlyLogged;
12
- private loggingMessages;
13
- constructor(config: ChannelStoreConfig);
14
- /**
15
- * Get or create the directory for a channel/DM
16
- */
17
- getChannelDir(channelId: string): string;
18
- /**
19
- * Generate a unique local filename for an attachment
20
- */
21
- generateLocalFilename(originalName: string, timestamp: string): string;
22
- /**
23
- * Process attachments from a Slack message event.
24
- * Downloads files before returning so callers only receive readable paths.
25
- */
26
- processAttachments(channelId: string, files: Array<{
27
- name?: string;
28
- url_private_download?: string;
29
- url_private?: string;
30
- }>, timestamp: string): Promise<Attachment[]>;
31
- /**
32
- * Log a message to the channel's log.jsonl
33
- * Returns false if message was already logged (duplicate)
34
- */
35
- logMessage(channelId: string, message: LoggedMessage): Promise<boolean>;
36
- /**
37
- * Log a bot response
38
- */
39
- logBotResponse(channelId: string, text: string, ts: string): Promise<void>;
40
- /**
41
- * Get the timestamp of the last logged message for a channel
42
- * Returns null if no log exists
43
- */
44
- getLastTimestamp(channelId: string): string | null;
45
- /**
46
- * Download a single attachment
47
- */
48
- private downloadAttachmentWithRetry;
49
- private downloadAttachment;
50
- }
51
- //# sourceMappingURL=store.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAUA,YAAY,EAAE,UAAU,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChF,OAAO,KAAK,EAAE,UAAU,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAkBhF;;;;GAIG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,QAAQ,CAAS;IAGzB,OAAO,CAAC,cAAc,CAA6B;IACnD,OAAO,CAAC,eAAe,CAAqB;IAE5C,YAAY,MAAM,EAAE,kBAAkB,EAMrC;IAED;;OAEG;IACH,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEvC;IAED;;OAEG;IACH,qBAAqB,CAAC,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAMrE;IAED;;;OAGG;IACG,kBAAkB,CACtB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,oBAAoB,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,EACpF,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,UAAU,EAAE,CAAC,CAiCvB;IAED;;;OAGG;IACG,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,CAoC5E;IAED;;OAEG;IACG,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAS/E;IAED;;;OAGG;IACH,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAyBjD;IAED;;OAEG;YACW,2BAA2B;YAQ3B,kBAAkB;CAuBjC"}