1
0
Эх сурвалжийг харах

feat: single-source projection keys via domain ./client/types pure outlets

imccyu 2 сар өмнө
parent
commit
75ea589976

+ 5 - 0
packages/session-title/session-title/package.json

@@ -15,12 +15,17 @@
       "types": "./lib/types/invariant.d.ts",
       "default": "./lib/invariant.js"
     },
+    "./client/types": {
+      "types": "./lib/types/client/types.d.ts",
+      "default": "./lib/types/client/types.js"
+    },
     "./src/*": "./src/*",
     "./package.json": "./package.json"
   },
   "files": [
     "lib/index.js",
     "lib/invariant.js",
+    "lib/types/**/*.js",
     "lib/types/**/*.d.ts",
     "lib/types/**/*.d.ts.map",
     "src"

+ 25 - 0
packages/session-title/session-title/src/client/types.ts

@@ -0,0 +1,25 @@
+/**
+ * Pure-type client outlet of the title domain: the ONE home of the `title`
+ * projection-key declaration, importable from client aggregates without
+ * dragging this package's host-side value imports (cordis service,
+ * schemastery, the llm seam). The host entry (`index.ts`) imports this module
+ * type-only to reuse the same merge — one declaration serves both program
+ * sides.
+ *
+ * @module @deepseek-ai/dsh-session-title/client/types
+ */
+
+// Marks this file a module so the declaration below AUGMENTS the projection
+// table instead of declaring an ambient module.
+export {}
+
+declare module '@deepseek-ai/dsh-session-projection/types' {
+  interface SessionProjectionMap {
+    /**
+     * The session's current normalized title — the latest `session/title`
+     * event's text (last-wins), or `null` before the first title lands. A
+     * plain string: the shape the client list rows consume.
+     */
+    title: string | null
+  }
+}

+ 6 - 11
packages/session-title/session-title/src/index.ts

@@ -17,6 +17,12 @@ import type {
 } from '@deepseek-ai/dsh-session'
 // Type-only: resolves ctx.sessionProjections for the optional unit child.
 import type {} from '@deepseek-ai/dsh-session-projection'
+// The `title` projection-key declaration lives in the client outlet (its one
+// home). A PLAIN side-effect import, not `import type`: declaration emit
+// elides type-only imports, and the aggregate programs resolve this package
+// through its emitted declarations — the merge must survive in index.d.ts.
+// The imported module is types-only, so the runtime edge is an empty module.
+import './client/types.ts'
 import { fallbackSessionTitle, normalizeSessionTitle } from './normalize.ts'
 
 export { fallbackSessionTitle, normalizeSessionTitle, truncateTitleUtf8 } from './normalize.ts'
@@ -103,17 +109,6 @@ declare module '@deepseek-ai/dsh-session' {
   }
 }
 
-declare module '@deepseek-ai/dsh-session-projection/types' {
-  interface SessionProjectionMap {
-    /**
-     * The session's current normalized title — the latest `session/title`
-     * event's text (last-wins), or `null` before the first title lands. A
-     * plain string: the shape the client list rows consume.
-     */
-    title: string | null
-  }
-}
-
 /** Per-session settlement tails for title-capability out-of-band writes. */
 const SESSION_TITLE_WRITE_TAILS = new WeakMap<Session, Promise<void>>()
 

+ 5 - 0
packages/todo/tool-todo/package.json

@@ -15,12 +15,17 @@
       "types": "./lib/types/invariant.d.ts",
       "default": "./lib/invariant.js"
     },
+    "./client/types": {
+      "types": "./lib/types/client/types.d.ts",
+      "default": "./lib/types/client/types.js"
+    },
     "./src/*": "./src/*",
     "./package.json": "./package.json"
   },
   "files": [
     "lib/index.js",
     "lib/invariant.js",
+    "lib/types/**/*.js",
     "lib/types/**/*.d.ts",
     "lib/types/**/*.d.ts.map",
     "src"

+ 24 - 0
packages/todo/tool-todo/src/client/types.ts

@@ -0,0 +1,24 @@
+/**
+ * Pure-type client outlet of the todo domain: the ONE home of the `todos`
+ * projection-key declaration, importable from client aggregates without
+ * dragging this package's host-side value imports (dsh-tools, zod). The host
+ * entry (`index.ts`) imports this module type-only to reuse the same merge —
+ * one declaration serves both program sides.
+ *
+ * @module @deepseek-ai/dsh-tool-todo/client/types
+ */
+
+import type { TodoItem } from '@deepseek-ai/dsh-session/types'
+
+export type { TodoItem } from '@deepseek-ai/dsh-session/types'
+
+declare module '@deepseek-ai/dsh-session-projection/types' {
+  interface SessionProjectionMap {
+    /**
+     * The agent's current whole todo list (the latest `todo/write` snapshot),
+     * or `null` before the first write. Whole-value rule: every `todo/write`
+     * carries the complete replacement list, so the fold is last-wins.
+     */
+    todos: TodoItem[] | null
+  }
+}

+ 6 - 11
packages/todo/tool-todo/src/index.ts

@@ -12,17 +12,12 @@ import { defineTool } from '@deepseek-ai/dsh-tools'
 import type { TodoItem } from '@deepseek-ai/dsh-session'
 // Type-only: resolves ctx.sessionProjections for the optional unit child.
 import type {} from '@deepseek-ai/dsh-session-projection'
-
-declare module '@deepseek-ai/dsh-session-projection/types' {
-  interface SessionProjectionMap {
-    /**
-     * The agent's current whole todo list (the latest `todo/write` snapshot),
-     * or `null` before the first write. Whole-value rule: every `todo/write`
-     * carries the complete replacement list, so the fold is last-wins.
-     */
-    todos: TodoItem[] | null
-  }
-}
+// The `todos` projection-key declaration lives in the client outlet (its one
+// home). A PLAIN side-effect import, not `import type`: declaration emit
+// elides type-only imports, and the aggregate programs resolve this package
+// through its emitted declarations — the merge must survive in index.d.ts.
+// The imported module is types-only, so the runtime edge is an empty module.
+import './client/types.ts'
 
 export const name = 'tool-todo'
 export const inject = ['tools']

+ 2 - 0
tsconfig.base.json

@@ -42,6 +42,8 @@
       "@deepseek-ai/dsh-session/types": ["./packages/core/session/src/types.ts"],
       "@deepseek-ai/dsh-session/surface": ["./packages/core/session/src/surface.ts"],
       "@deepseek-ai/dsh-session-projection/types": ["./packages/session-projection/session-projection/src/types.ts"],
+      "@deepseek-ai/dsh-tool-todo/client/types": ["./packages/todo/tool-todo/src/client/types.ts"],
+      "@deepseek-ai/dsh-session-title/client/types": ["./packages/session-title/session-title/src/client/types.ts"],
       "@deepseek-ai/dsh-llm/types": ["./packages/llm/llm/src/types.ts"],
       "@deepseek-ai/dsh-llm/brand": ["./packages/llm/llm/src/brand.ts"],
       "@deepseek-ai/dsh-tools/presentation": ["./packages/core/tools/src/presentation.ts"],