feat: todos session-projection provider in tool-todo (knife-4 domain probe)
This commit is contained in:
@@ -0,0 +1,92 @@
|
|||||||
|
/**
|
||||||
|
* Knife-4 acceptance probe (session-projection RFC): the todo domain's client
|
||||||
|
* cell — `fromEvent: todo/write ⇒ whole list` — runs end to end on the
|
||||||
|
* UNMODIFIED cell framework: baseline seeding from a history response's
|
||||||
|
* projections block, live last-wins folding, and the seq guard, with the
|
||||||
|
* `todos` key merged test-locally the same way the domain client plugin will
|
||||||
|
* (through the interface package's pure-type outlet). Zero framework edits.
|
||||||
|
*/
|
||||||
|
import { describe, expect, it } from 'vitest'
|
||||||
|
import type { SessionEvent } from '@deepseek-ai/dsh-session/types'
|
||||||
|
import type { TodoItem } from '@deepseek-ai/dsh-session/types'
|
||||||
|
import type { SessionId } from '@deepseek-ai/dsh-client-connection/client'
|
||||||
|
import type { ProjectionCellSpec } from '../src/client/sessions/projection-cell.ts'
|
||||||
|
import { Session } from '../src/client/sessions/session.ts'
|
||||||
|
import { FakeApiClient, ok } from './fake-api.ts'
|
||||||
|
import { entries, plainTurn } from './event-script.ts'
|
||||||
|
|
||||||
|
declare module '@deepseek-ai/dsh-session-projection/types' {
|
||||||
|
interface SessionProjectionMap {
|
||||||
|
todos: TodoItem[] | null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const SID = 'fk-todo' as SessionId
|
||||||
|
|
||||||
|
const todoEvent = (seq: number, todos: TodoItem[]): SessionEvent =>
|
||||||
|
({ seq, time: 1_700_000_000_000 + seq, type: 'todo/write', data: { todos } }) as unknown as SessionEvent
|
||||||
|
|
||||||
|
/** The exact cell the todo domain client plugin will register: whole-list fromEvent, array-or-null schema. */
|
||||||
|
const todosSpec = (): ProjectionCellSpec<'todos'> => ({
|
||||||
|
key: 'todos',
|
||||||
|
schema: {
|
||||||
|
parse: (value) => {
|
||||||
|
if (value === null || Array.isArray(value)) return value as TodoItem[] | null
|
||||||
|
throw new Error('not a todos payload')
|
||||||
|
},
|
||||||
|
},
|
||||||
|
fromEvent: event => (event.type === 'todo/write'
|
||||||
|
? (event as unknown as { data: { todos: TodoItem[] } }).data.todos
|
||||||
|
: undefined),
|
||||||
|
})
|
||||||
|
|
||||||
|
function makeSession() {
|
||||||
|
const api = new FakeApiClient()
|
||||||
|
const session = new Session(SID, api)
|
||||||
|
session.projections.register(todosSpec())
|
||||||
|
const cell = session.projections.cellOf('todos')
|
||||||
|
if (cell === undefined) throw new Error('cell missing after register')
|
||||||
|
return { api, session, cell }
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('todo projection cell over the unmodified framework', () => {
|
||||||
|
it('seeds null from a pre-first-write baseline, then a live todo/write replaces it whole', async () => {
|
||||||
|
const { api, session, cell } = makeSession()
|
||||||
|
api.onHistory = () => Promise.resolve(ok({
|
||||||
|
events: entries(plainTurn(0, 0, 'q', 'a')) as never[], hasMore: false,
|
||||||
|
projections: { asOfSeq: 5, values: { todos: null } },
|
||||||
|
} as never))
|
||||||
|
await session.open()
|
||||||
|
expect(cell.getSnapshot()).toBeNull()
|
||||||
|
const list: TodoItem[] = [{ content: 'ship knife 4', status: 'in_progress' }]
|
||||||
|
session.handleMuxEnvelope('r' as never, { type: 'session/event', sessionId: SID, event: todoEvent(6, list) })
|
||||||
|
expect(cell.getSnapshot()).toEqual(list)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('seeds the whole list from the baseline and drops a replayed older snapshot (last-wins)', async () => {
|
||||||
|
const { api, session, cell } = makeSession()
|
||||||
|
const current: TodoItem[] = [
|
||||||
|
{ content: 'a', status: 'completed' },
|
||||||
|
{ content: 'b', status: 'pending' },
|
||||||
|
]
|
||||||
|
api.onHistory = () => Promise.resolve(ok({
|
||||||
|
events: entries(plainTurn(0, 0, 'q', 'a')) as never[], hasMore: false,
|
||||||
|
projections: { asOfSeq: 9, values: { todos: current } },
|
||||||
|
} as never))
|
||||||
|
await session.open()
|
||||||
|
expect(cell.getSnapshot()).toEqual(current)
|
||||||
|
// A replayed pre-cut write (window path) must not roll the list back.
|
||||||
|
session.projections.offerWindow([todoEvent(4, [{ content: 'stale', status: 'pending' }])])
|
||||||
|
expect(cell.getSnapshot()).toEqual(current)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('reads capability-absent (undefined) when the block omits the todos key', async () => {
|
||||||
|
const { api, session, cell } = makeSession()
|
||||||
|
api.onHistory = () => Promise.resolve(ok({
|
||||||
|
events: entries(plainTurn(0, 0, 'q', 'a')) as never[], hasMore: false,
|
||||||
|
projections: { asOfSeq: 5, values: {} },
|
||||||
|
} as never))
|
||||||
|
await session.open()
|
||||||
|
expect(cell.getSnapshot()).toBeUndefined()
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -22,6 +22,10 @@ Beyond the schema's type/required/enum checks, `execute` rejects an empty or dup
|
|||||||
|
|
||||||
The canonical result is `{ todos, counts: { pending, inProgress, completed } }`; its Native renderer returns the compact update acknowledgement. The tool also writes the full `todo/write` session event. UIs subscribe to the event stream and render that durable list themselves: the [TUI app](../../examples/tui-demo) shows it as a persistent plan, and the [web client](../../client/ui-conversation) renders a plan strip plus a dedicated tool row off `ConversationSnapshot.todos` ([Agent Note](../../../.agents/notes/implemented/feature/2026-07-23-web-todo-display.md)).
|
The canonical result is `{ todos, counts: { pending, inProgress, completed } }`; its Native renderer returns the compact update acknowledgement. The tool also writes the full `todo/write` session event. UIs subscribe to the event stream and render that durable list themselves: the [TUI app](../../examples/tui-demo) shows it as a persistent plan, and the [web client](../../client/ui-conversation) renders a plan strip plus a dedicated tool row off `ConversationSnapshot.todos` ([Agent Note](../../../.agents/notes/implemented/feature/2026-07-23-web-todo-display.md)).
|
||||||
|
|
||||||
|
## Session projection
|
||||||
|
|
||||||
|
When the composition mounts `ctx.sessionProjections` ([`@deepseek-ai/dsh-session-projection`](../../session-projection/session-projection/README.md)), this package registers the `todos` provider under an injected child: value = the latest `todo/write` snapshot backscanned from the in-memory log tail (whole list, last-wins), `null` before the first write. The key merges into `SessionProjectionMap` here (via the interface package's `/types` outlet); carriers serve it on the history tail page. Compositions without the registry are unaffected.
|
||||||
|
|
||||||
## Export shape
|
## Export shape
|
||||||
|
|
||||||
A function/namespace plugin: it exports `name` / `inject` / `apply` and NO default. A stray `export default` would collapse the module via the Loader's `unwrapExports` and drop `inject` (see [docs/postmortem/0001](../../../docs/postmortem/0001-acp-default-export-drops-inject.md)).
|
A function/namespace plugin: it exports `name` / `inject` / `apply` and NO default. A stray `export default` would collapse the module via the Loader's `unwrapExports` and drop `inject` (see [docs/postmortem/0001](../../../docs/postmortem/0001-acp-default-export-drops-inject.md)).
|
||||||
|
|||||||
@@ -26,10 +26,14 @@
|
|||||||
"src"
|
"src"
|
||||||
],
|
],
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"zod": "^4.4.3"
|
||||||
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@deepseek-ai/dsh-agent": "^0.0.1",
|
"@deepseek-ai/dsh-agent": "^0.0.1",
|
||||||
"@deepseek-ai/dsh-invariants": "^0.0.1",
|
"@deepseek-ai/dsh-invariants": "^0.0.1",
|
||||||
"@deepseek-ai/dsh-session": "^0.0.1",
|
"@deepseek-ai/dsh-session": "^0.0.1",
|
||||||
|
"@deepseek-ai/dsh-session-projection": "^0.0.1",
|
||||||
"@deepseek-ai/dsh-tools": "^0.0.1",
|
"@deepseek-ai/dsh-tools": "^0.0.1",
|
||||||
"cordis": "^4.0.0-rc.7"
|
"cordis": "^4.0.0-rc.7"
|
||||||
},
|
},
|
||||||
@@ -37,11 +41,14 @@
|
|||||||
"@deepseek-ai/dsh-agent": "workspace:^",
|
"@deepseek-ai/dsh-agent": "workspace:^",
|
||||||
"@deepseek-ai/dsh-agent-loop": "workspace:^",
|
"@deepseek-ai/dsh-agent-loop": "workspace:^",
|
||||||
"@deepseek-ai/dsh-agent-loop-testkit": "workspace:^",
|
"@deepseek-ai/dsh-agent-loop-testkit": "workspace:^",
|
||||||
|
"@deepseek-ai/dsh-host-apiproxy": "workspace:^",
|
||||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||||
"@deepseek-ai/dsh-llm": "workspace:^",
|
"@deepseek-ai/dsh-llm": "workspace:^",
|
||||||
"@deepseek-ai/dsh-session": "workspace:^",
|
"@deepseek-ai/dsh-session": "workspace:^",
|
||||||
|
"@deepseek-ai/dsh-session-projection": "workspace:^",
|
||||||
"@deepseek-ai/dsh-system-prompt": "workspace:^",
|
"@deepseek-ai/dsh-system-prompt": "workspace:^",
|
||||||
"@deepseek-ai/dsh-tools": "workspace:^",
|
"@deepseek-ai/dsh-tools": "workspace:^",
|
||||||
|
"@deepseek-ai/dsh-user-interaction": "workspace:^",
|
||||||
"cordis": "^4.0.0-rc.7"
|
"cordis": "^4.0.0-rc.7"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,24 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Context } from 'cordis'
|
import type { Context } from 'cordis'
|
||||||
|
import { z } from 'zod'
|
||||||
|
import type { ZodType } from 'zod'
|
||||||
import { defineTool } from '@deepseek-ai/dsh-tools'
|
import { defineTool } from '@deepseek-ai/dsh-tools'
|
||||||
import type { TodoItem } from '@deepseek-ai/dsh-session'
|
import type { Agent } from '@deepseek-ai/dsh-agent'
|
||||||
|
import type { SessionEvent, TodoItem } from '@deepseek-ai/dsh-session'
|
||||||
|
// Type-only: resolves ctx.sessionProjections for the optional provider 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const name = 'tool-todo'
|
export const name = 'tool-todo'
|
||||||
export const inject = ['tools']
|
export const inject = ['tools']
|
||||||
@@ -57,8 +73,40 @@ function toTodoList(raw: { content: string; status: string }[]): TodoItem[] {
|
|||||||
return todos
|
return todos
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Register the `todo_write` tool on `ctx.tools`. */
|
/** Wire payload schema of the `todos` projection (whole list or pre-first-write null). */
|
||||||
|
const todosProjectionSchema: ZodType<TodoItem[] | null> = z.union([
|
||||||
|
z.array(z.object({
|
||||||
|
content: z.string(),
|
||||||
|
status: z.union([z.literal('pending'), z.literal('in_progress'), z.literal('completed')]),
|
||||||
|
})),
|
||||||
|
z.null(),
|
||||||
|
])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current whole todo list: the latest `todo/write` snapshot, backscanned from
|
||||||
|
* the log tail (bounded: first hit terminates; the events live in memory).
|
||||||
|
* `null` = no write yet.
|
||||||
|
*/
|
||||||
|
function currentTodos(agent: Agent): TodoItem[] | null {
|
||||||
|
const events = agent.session.events
|
||||||
|
for (let i = events.length - 1; i >= 0; i--) {
|
||||||
|
const event = events[i] as SessionEvent
|
||||||
|
if (event.type === 'todo/write') return event.data.todos
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Register the `todo_write` tool on `ctx.tools` and, when the session-projection seam is composed, the `todos` provider. */
|
||||||
export function apply(ctx: Context): void {
|
export function apply(ctx: Context): void {
|
||||||
|
// The provider child activates only when a projection registry is composed
|
||||||
|
// (headless assemblies without the seam stay unaffected).
|
||||||
|
ctx.inject(['sessionProjections'], (projectionCtx) => {
|
||||||
|
projectionCtx.sessionProjections.register({
|
||||||
|
key: 'todos',
|
||||||
|
schema: todosProjectionSchema,
|
||||||
|
get: currentTodos,
|
||||||
|
})
|
||||||
|
})
|
||||||
ctx.tools.register(defineTool({
|
ctx.tools.register(defineTool({
|
||||||
name: 'todo_write',
|
name: 'todo_write',
|
||||||
description: DESCRIPTION,
|
description: DESCRIPTION,
|
||||||
|
|||||||
@@ -0,0 +1,106 @@
|
|||||||
|
/**
|
||||||
|
* The `todos` projection provider (session-projection RFC knife 4 — the "a
|
||||||
|
* fourth domain is just its own registrations" acceptance probe): mounting
|
||||||
|
* tool-todo beside the registry serves the whole current list on the history
|
||||||
|
* tail page with a consistent asOfSeq; before any write the value is null; a
|
||||||
|
* composition without tool-todo has no `todos` key; unmounting tool-todo
|
||||||
|
* removes it (HMR safety). The carrier and framework are exercised unmodified.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { describe, expect, it } from 'vitest'
|
||||||
|
import { Context } from 'cordis'
|
||||||
|
import AgentRegistry from '@deepseek-ai/dsh-agent'
|
||||||
|
import type { Agent } from '@deepseek-ai/dsh-agent'
|
||||||
|
import SessionStore from '@deepseek-ai/dsh-session'
|
||||||
|
import type { Session, TodoItem } from '@deepseek-ai/dsh-session'
|
||||||
|
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
|
||||||
|
import ToolRegistry from '@deepseek-ai/dsh-tools'
|
||||||
|
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
|
||||||
|
import UserInteractionService from '@deepseek-ai/dsh-user-interaction'
|
||||||
|
import type { RpcRequest } from '@deepseek-ai/dsh-host-apiproxy/api/rpc'
|
||||||
|
import { RpcId } from '@deepseek-ai/dsh-host-apiproxy/api/rpc'
|
||||||
|
import { createApiProxy } from '@deepseek-ai/dsh-host-apiproxy'
|
||||||
|
import * as ToolTodo from '@deepseek-ai/dsh-tool-todo'
|
||||||
|
|
||||||
|
let nextRpc = 1
|
||||||
|
function request<P>(payload: P): RpcRequest<P> {
|
||||||
|
return { rpcId: RpcId(`todo-proj-${String(nextRpc++)}`), payload }
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Bench {
|
||||||
|
ctx: Context
|
||||||
|
session: Session
|
||||||
|
tailProjections(): Promise<{ asOfSeq: number; values: Record<string, unknown> } | undefined>
|
||||||
|
}
|
||||||
|
|
||||||
|
async function harness(withTodoTool: boolean): Promise<Bench> {
|
||||||
|
const ctx = new Context()
|
||||||
|
await ctx.plugin(SessionStore)
|
||||||
|
await ctx.plugin(SystemPrompt, { persona: '' })
|
||||||
|
await ctx.plugin(ToolRegistry)
|
||||||
|
await ctx.plugin(UserInteractionService)
|
||||||
|
await ctx.plugin(AgentRegistry)
|
||||||
|
await ctx.plugin(SessionProjectionRegistry)
|
||||||
|
if (withTodoTool) await ctx.plugin(ToolTodo)
|
||||||
|
const session = ctx.sessions.create()
|
||||||
|
ctx.agents.register({ id: session.id, session, status: 'idle', ctx } as Agent)
|
||||||
|
const api = createApiProxy(ctx, { provider: 'p', model: 'm', cwd: '/tmp', workspaceRoot: '/tmp' })
|
||||||
|
return {
|
||||||
|
ctx,
|
||||||
|
session,
|
||||||
|
async tailProjections() {
|
||||||
|
const response = await api.sessions.history(request({ sessionId: session.id }))
|
||||||
|
if (!response.result.ok) throw new Error('history failed')
|
||||||
|
return response.result.value.projections as { asOfSeq: number; values: Record<string, unknown> } | undefined
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** One paginable message so the tail page is non-degenerate. */
|
||||||
|
function seedMessage(session: Session): void {
|
||||||
|
session.append('user/message', { content: [{ type: 'text', text: 'hi' }], source: { kind: 'user' } }, { surfaceOp: 'append' })
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('todos projection provider', () => {
|
||||||
|
it('serves null before the first todo/write', async () => {
|
||||||
|
const bench = await harness(true)
|
||||||
|
seedMessage(bench.session)
|
||||||
|
const projections = await bench.tailProjections()
|
||||||
|
expect(projections?.values).toEqual({ todos: null })
|
||||||
|
expect(projections?.asOfSeq).toBe(bench.session.seq)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('serves the latest whole list after writes, asOfSeq = window tail seq', async () => {
|
||||||
|
const bench = await harness(true)
|
||||||
|
const session = bench.session
|
||||||
|
seedMessage(session)
|
||||||
|
const first: TodoItem[] = [{ content: 'a', status: 'pending' }]
|
||||||
|
const second: TodoItem[] = [
|
||||||
|
{ content: 'a', status: 'completed' },
|
||||||
|
{ content: 'b', status: 'in_progress' },
|
||||||
|
]
|
||||||
|
session.append('todo/write', { todos: first })
|
||||||
|
session.append('todo/write', { todos: second })
|
||||||
|
const projections = await bench.tailProjections()
|
||||||
|
// Last-wins: the latest snapshot, whole.
|
||||||
|
expect(projections?.values.todos).toEqual(second)
|
||||||
|
expect(projections?.asOfSeq).toBe(session.seq)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has no todos key when tool-todo is not composed', async () => {
|
||||||
|
const bench = await harness(false)
|
||||||
|
seedMessage(bench.session)
|
||||||
|
const projections = await bench.tailProjections()
|
||||||
|
expect(projections).toBeDefined()
|
||||||
|
expect('todos' in (projections?.values ?? {})).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('drops the key when the tool-todo fiber unloads (HMR safety)', async () => {
|
||||||
|
const bench = await harness(false)
|
||||||
|
seedMessage(bench.session)
|
||||||
|
const fiber = await bench.ctx.plugin(ToolTodo)
|
||||||
|
expect((await bench.tailProjections())?.values).toEqual({ todos: null })
|
||||||
|
await fiber.dispose()
|
||||||
|
expect('todos' in ((await bench.tailProjections())?.values ?? {})).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -23,6 +23,9 @@
|
|||||||
{
|
{
|
||||||
"path": "../../core/session"
|
"path": "../../core/session"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "../../session-projection/session-projection"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "../../support/invariants"
|
"path": "../../support/invariants"
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+16
@@ -874,6 +874,9 @@ importers:
|
|||||||
'@deepseek-ai/dsh-session':
|
'@deepseek-ai/dsh-session':
|
||||||
specifier: workspace:^
|
specifier: workspace:^
|
||||||
version: link:../../core/session
|
version: link:../../core/session
|
||||||
|
'@deepseek-ai/dsh-session-projection':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../../session-projection/session-projection
|
||||||
immer:
|
immer:
|
||||||
specifier: ^10.1.1
|
specifier: ^10.1.1
|
||||||
version: 10.2.0
|
version: 10.2.0
|
||||||
@@ -4269,6 +4272,10 @@ importers:
|
|||||||
version: 4.0.0-rc.7(@cordisjs/plugin-include@1.0.4)(@cordisjs/plugin-loader@1.0.0-rc.5)
|
version: 4.0.0-rc.7(@cordisjs/plugin-include@1.0.4)(@cordisjs/plugin-loader@1.0.0-rc.5)
|
||||||
|
|
||||||
packages/todo/tool-todo:
|
packages/todo/tool-todo:
|
||||||
|
dependencies:
|
||||||
|
zod:
|
||||||
|
specifier: ^4.4.3
|
||||||
|
version: 4.4.3
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@deepseek-ai/dsh-agent':
|
'@deepseek-ai/dsh-agent':
|
||||||
specifier: workspace:^
|
specifier: workspace:^
|
||||||
@@ -4279,6 +4286,9 @@ importers:
|
|||||||
'@deepseek-ai/dsh-agent-loop-testkit':
|
'@deepseek-ai/dsh-agent-loop-testkit':
|
||||||
specifier: workspace:^
|
specifier: workspace:^
|
||||||
version: link:../../support/agent-loop-testkit
|
version: link:../../support/agent-loop-testkit
|
||||||
|
'@deepseek-ai/dsh-host-apiproxy':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../../host/apiproxy
|
||||||
'@deepseek-ai/dsh-invariants':
|
'@deepseek-ai/dsh-invariants':
|
||||||
specifier: workspace:^
|
specifier: workspace:^
|
||||||
version: link:../../support/invariants
|
version: link:../../support/invariants
|
||||||
@@ -4288,12 +4298,18 @@ importers:
|
|||||||
'@deepseek-ai/dsh-session':
|
'@deepseek-ai/dsh-session':
|
||||||
specifier: workspace:^
|
specifier: workspace:^
|
||||||
version: link:../../core/session
|
version: link:../../core/session
|
||||||
|
'@deepseek-ai/dsh-session-projection':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../../session-projection/session-projection
|
||||||
'@deepseek-ai/dsh-system-prompt':
|
'@deepseek-ai/dsh-system-prompt':
|
||||||
specifier: workspace:^
|
specifier: workspace:^
|
||||||
version: link:../../core/system-prompt
|
version: link:../../core/system-prompt
|
||||||
'@deepseek-ai/dsh-tools':
|
'@deepseek-ai/dsh-tools':
|
||||||
specifier: workspace:^
|
specifier: workspace:^
|
||||||
version: link:../../core/tools
|
version: link:../../core/tools
|
||||||
|
'@deepseek-ai/dsh-user-interaction':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../../ui/user-interaction
|
||||||
cordis:
|
cordis:
|
||||||
specifier: ^4.0.0-rc.7
|
specifier: ^4.0.0-rc.7
|
||||||
version: 4.0.0-rc.7(@cordisjs/plugin-include@1.0.4)(@cordisjs/plugin-loader@1.0.0-rc.5)
|
version: 4.0.0-rc.7(@cordisjs/plugin-include@1.0.4)(@cordisjs/plugin-loader@1.0.0-rc.5)
|
||||||
|
|||||||
Reference in New Issue
Block a user