refactor: apply repository naming contract
Apply the accepted pre-release package, service, type, directory, and role renames as one repository-wide change.
This commit is contained in:
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write packages/feedback/command-feedback/README.md
|
||||
README.md: 24a975476b6783b439d4ec94c449f2acbe0b432f
|
||||
README.zh.md: 74e493f9b98df679dc19f581a14f5faee6780be4
|
||||
README.md: e3b67e8ecf582681968a4d009b07ee90abbb66a8
|
||||
README.zh.md: 5e2ba29caa271f3a4ef8e147e39dc9eb07c5085f
|
||||
|
||||
@@ -8,14 +8,14 @@ Trigger-independent session feedback plus human-facing `/feedback` capture. The
|
||||
|
||||
| Input | Result |
|
||||
|---|---|
|
||||
| `/feedback <text>` | Append `feedback/record` and acknowledge with `Feedback recorded for session {sessionId}`, `User: {userId}`, plus the session-sharing disclosure. |
|
||||
| `/feedback <text>` | Append `feedback/record` and acknowledge with `Feedback recorded for session {sessionId}`, `Anonymous user: {userId}`, plus the session-sharing disclosure. |
|
||||
| `/feedback` | Return a direct usage error. Whitespace-only input is treated as empty. |
|
||||
|
||||
Surrounding whitespace is discarded, but feedback is otherwise unparsed: no truncation, case folding, or control words. Text that looks like another command, such as `/feedback /plan felt slow`, is feedback content. Repeated commands each produce their own event; nothing is replaced or merged.
|
||||
|
||||
## Session-sharing disclosure
|
||||
|
||||
The acknowledgement names the receiving session id and reports how that session is shared, read from the mounted [`telemetry`](../../session/session-telemetry/README.md) service through the plugin context (`ctx.get('telemetry')`, never a declared injection). The disclosure is one sentence chosen from the backend's [`TelemetrySharingStatus`](../../session/session-telemetry/README.md):
|
||||
The acknowledgement names the receiving session id and reports how that session is shared, read from the mounted [`telemetry`](../../session/session-telemetry/README.md) service through the plugin context (`ctx.get('telemetry')`, never a declared injection). The disclosure is one sentence chosen from the backend's [`SessionTelemetrySharingStatus`](../../session/session-telemetry/README.md):
|
||||
|
||||
| Disclosed status | Acknowledgement sentence |
|
||||
|---|---|
|
||||
@@ -30,7 +30,7 @@ The disclosure states the deployment's current sharing policy only; it never pro
|
||||
|
||||
`recordFeedback(session, text)` is the command-independent write path. It rejects empty normalized text and appends `feedback/record { text }`; a different UI, hook, or host integration can call it without constructing a slash command. The `/feedback` handler uses that producer and starts no model work. The optional [`dsh-session-telemetry-otel`](../../session/session-telemetry-otel) consumer observes the event without changing its capture contract.
|
||||
|
||||
The feedback text appears in exactly one durable payload: `feedback/record`. [`dsh-commands`](../../interaction/commands/README.md) still appends its generic `command/run` / `command/done` pairing, but this definition sets `recordInput: false`, so `command/run` omits `args`; the paired `command/done` carries only the outcome. All three events are log-only and absent from the ordered surface, `deriveMessages()`, and model requests. These appends start persistence's ordinary eager drain, but neither producer forces `session/flush`, so acknowledgement means the feedback is in the log, not that it has reached disk. The acknowledgement identifies both the receiving session and the [shared anonymous user](../../session/user-id/); the first accepted feedback for a harness home can create `$DSH_HOME/.userid`. Rejected empty input leaves only the command pairing settled as `kind: 'error'`, with no `feedback/record` and no user-id lookup.
|
||||
The feedback text appears in exactly one durable payload: `feedback/record`. [`dsh-commands`](../../interaction/commands/README.md) still appends its generic `command/run` / `command/done` pairing, but this definition sets `recordInput: false`, so `command/run` omits `args`; the paired `command/done` carries only the outcome. All three events are log-only and absent from the ordered surface, `deriveMessages()`, and model requests. These appends start persistence's ordinary eager drain, but neither producer forces `session/flush`, so acknowledgement means the feedback is in the log, not that it has reached disk. The acknowledgement identifies both the receiving session and the [shared anonymous user](../../identity/anonymous-user-id/); the first accepted feedback for a harness home can create `$DSH_HOME/.anonymous-user-id`. Rejected empty input leaves only the command pairing settled as `kind: 'error'`, with no `feedback/record` and no user-id lookup.
|
||||
|
||||
The event is authoritative rather than the command record because feedback may arrive through a trigger other than `/feedback`. Keeping the payload out of `command/run` avoids two records carrying the same text.
|
||||
|
||||
|
||||
@@ -8,14 +8,14 @@
|
||||
|
||||
| 输入 | 结果 |
|
||||
|---|---|
|
||||
| `/feedback <text>` | 追加 `feedback/record`,并以 `Feedback recorded for session {sessionId}`、`User: {userId}` 加会话共享披露确认。 |
|
||||
| `/feedback <text>` | 追加 `feedback/record`,并以 `Feedback recorded for session {sessionId}`、`Anonymous user: {userId}` 加会话共享披露确认。 |
|
||||
| `/feedback` | 返回一个直接用法错误。仅含空白的输入视为空输入。 |
|
||||
|
||||
前后空白会被丢弃,但除此之外,反馈内容不会被解析:不进行截断或大小写折叠,也不识别控制词。看起来像另一个命令的文本(例如 `/feedback /plan felt slow`)就是反馈内容。重复执行命令时,每次都会产生一个事件;不会发生替换或合并。
|
||||
|
||||
## 会话共享披露
|
||||
|
||||
确认文本会点名接收会话的 id,并报告该会话如何被共享;该信息通过插件上下文(`ctx.get('telemetry')`,绝不是声明的注入)从已挂载的 [`telemetry`](../../session/session-telemetry/README.md) 服务读取。披露是依据后端 [`TelemetrySharingStatus`](../../session/session-telemetry/README.md) 选择的一句话:
|
||||
确认文本会点名接收会话的 id,并报告该会话如何被共享;该信息通过插件上下文(`ctx.get('telemetry')`,绝不是声明的注入)从已挂载的 [`telemetry`](../../session/session-telemetry/README.md) 服务读取。披露是依据后端 [`SessionTelemetrySharingStatus`](../../session/session-telemetry/README.md) 选择的一句话:
|
||||
|
||||
| 披露的状态 | 确认文本中的句子 |
|
||||
|---|---|
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
`recordFeedback(session, text)` 是不依赖命令的写入路径。它拒绝规范化后为空的文本,并追加 `feedback/record { text }`;其他 UI、钩子或 host 集成无需构造斜杠命令即可调用它。`/feedback` 处理器通过该函数写入,且不启动任何模型工作。可选的 [`dsh-session-telemetry-otel`](../../session/session-telemetry-otel) 消费方会观察该事件,但不改变它的采集约定。
|
||||
|
||||
反馈文本只出现在一个持久载荷中:`feedback/record`。[`dsh-commands`](../../interaction/commands/README.md) 仍会追加通用的 `command/run` / `command/done` 配对,但此定义设置了 `recordInput: false`,因此 `command/run` 会省略 `args`;配对的 `command/done` 只携带结果。三个事件都仅写入日志,不出现在有序 surface、`deriveMessages()` 以及模型请求中。这些追加会启动持久化的常规即时排空,但两个生产方都不会强制 `session/flush`,因此确认文本表示反馈已进入日志,而不表示它已经落盘。确认文本同时标明接收反馈的会话和[共享匿名用户](../../session/user-id/);对于某个 harness home,首次接受反馈时可能创建 `$DSH_HOME/.userid`。被拒绝的空输入只会留下以 `kind: 'error'` 结算的命令配对,不会产生 `feedback/record`,也不会查找用户 id。
|
||||
反馈文本只出现在一个持久载荷中:`feedback/record`。[`dsh-commands`](../../interaction/commands/README.md) 仍会追加通用的 `command/run` / `command/done` 配对,但此定义设置了 `recordInput: false`,因此 `command/run` 会省略 `args`;配对的 `command/done` 只携带结果。三个事件都仅写入日志,不出现在有序 surface、`deriveMessages()` 以及模型请求中。这些追加会启动持久化的常规即时排空,但两个生产方都不会强制 `session/flush`,因此确认文本表示反馈已进入日志,而不表示它已经落盘。确认文本同时标明接收反馈的会话和[共享匿名用户](../../identity/anonymous-user-id/);对于某个 harness home,首次接受反馈时可能创建 `$DSH_HOME/.anonymous-user-id`。被拒绝的空输入只会留下以 `kind: 'error'` 结算的命令配对,不会产生 `feedback/record`,也不会查找用户 id。
|
||||
|
||||
权威记录是该事件,而不是命令记录,因为反馈可能来自 `/feedback` 之外的触发方式。让载荷不进入 `command/run`,可避免两条记录携带相同文本。
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@deepseek-ai/dsh-session": "workspace:^",
|
||||
"@deepseek-ai/dsh-session-telemetry": "workspace:^",
|
||||
"@deepseek-ai/dsh-user-id": "workspace:^",
|
||||
"@deepseek-ai/dsh-anonymous-user-id": "workspace:^",
|
||||
"@deepseek-ai/cordis": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -48,7 +48,7 @@
|
||||
"@deepseek-ai/dsh-llm": "workspace:^",
|
||||
"@deepseek-ai/dsh-session": "workspace:^",
|
||||
"@deepseek-ai/dsh-session-telemetry": "workspace:^",
|
||||
"@deepseek-ai/dsh-user-id": "workspace:^",
|
||||
"@deepseek-ai/dsh-anonymous-user-id": "workspace:^",
|
||||
"@deepseek-ai/cordis": "workspace:^"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import type { CommandInvocation, CommandResult } from '@deepseek-ai/dsh-commands'
|
||||
import type { Telemetry, TelemetrySharingStatus } from '@deepseek-ai/dsh-session-telemetry'
|
||||
import type { SessionTelemetryBackend, SessionTelemetrySharingStatus } from '@deepseek-ai/dsh-session-telemetry'
|
||||
import type { Session } from '@deepseek-ai/dsh-session'
|
||||
import { getOrCreateAnonymousUserId } from '@deepseek-ai/dsh-user-id'
|
||||
import { getOrCreateAnonymousUserId } from '@deepseek-ai/dsh-anonymous-user-id'
|
||||
|
||||
export const name = 'command-feedback'
|
||||
export const inject = ['commands']
|
||||
@@ -24,7 +24,7 @@ function assertNever(value: never): never {
|
||||
}
|
||||
|
||||
/** The acknowledgement's sharing sentence for a disclosed policy. */
|
||||
function sharingSentence(sharing: TelemetrySharingStatus): string {
|
||||
function sharingSentence(sharing: SessionTelemetrySharingStatus): string {
|
||||
switch (sharing) {
|
||||
case 'full':
|
||||
return 'Session sharing is enabled.'
|
||||
@@ -46,7 +46,7 @@ function sharingSentence(sharing: TelemetrySharingStatus): string {
|
||||
* @param telemetry - the mounted telemetry service, or undefined.
|
||||
* @returns one sentence describing this session's sharing policy.
|
||||
*/
|
||||
function sharingDisclosure(telemetry: Telemetry | undefined): string {
|
||||
function sharingDisclosure(telemetry: SessionTelemetryBackend | undefined): string {
|
||||
if (telemetry === undefined) {
|
||||
return 'Session sharing is not configured.'
|
||||
}
|
||||
@@ -89,10 +89,10 @@ function executeFeedbackCommand(invocation: CommandInvocation, ctx: Context): Co
|
||||
return { kind: 'error', text: `Feedback text is required. ${USAGE}` }
|
||||
}
|
||||
recordFeedback(invocation.agent.session, invocation.rawInput)
|
||||
const telemetry = ctx.get('telemetry')
|
||||
const telemetry = ctx.get('sessionTelemetry')
|
||||
return {
|
||||
kind: 'success',
|
||||
text: `Feedback recorded for session ${invocation.agent.session.id}\nUser: ${getOrCreateAnonymousUserId()}. ${sharingDisclosure(telemetry)}`,
|
||||
text: `Feedback recorded for session ${invocation.agent.session.id}\nAnonymous user: ${getOrCreateAnonymousUserId()}. ${sharingDisclosure(telemetry)}`,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@ import { Context } from '@deepseek-ai/cordis'
|
||||
import Loader from '@deepseek-ai/cordis-plugin-loader'
|
||||
import AgentRegistry, { Inbox } from '@deepseek-ai/dsh-agent'
|
||||
import type { Agent, AgentStatus } from '@deepseek-ai/dsh-agent'
|
||||
import CommandService from '@deepseek-ai/dsh-commands'
|
||||
import CommandRuntime from '@deepseek-ai/dsh-commands'
|
||||
import SessionStore, { foldSurface, Session, SessionId } from '@deepseek-ai/dsh-session'
|
||||
import { Telemetry, type TelemetrySharingStatus } from '@deepseek-ai/dsh-session-telemetry'
|
||||
import { SessionTelemetryBackend, type SessionTelemetrySharingStatus } from '@deepseek-ai/dsh-session-telemetry'
|
||||
import * as commandFeedback from '@deepseek-ai/dsh-command-feedback'
|
||||
|
||||
const { USER_ID, getOrCreateAnonymousUserId } = vi.hoisted(() => {
|
||||
@@ -13,7 +13,7 @@ const { USER_ID, getOrCreateAnonymousUserId } = vi.hoisted(() => {
|
||||
return { USER_ID, getOrCreateAnonymousUserId: vi.fn(() => USER_ID) }
|
||||
})
|
||||
|
||||
vi.mock('@deepseek-ai/dsh-user-id', () => ({
|
||||
vi.mock('@deepseek-ai/dsh-anonymous-user-id', () => ({
|
||||
getOrCreateAnonymousUserId,
|
||||
}))
|
||||
|
||||
@@ -27,10 +27,10 @@ interface Harness {
|
||||
}
|
||||
|
||||
/** Minimal mounted backend disclosing one sharing policy. */
|
||||
class FakeTelemetry extends Telemetry {
|
||||
override readonly sharing: TelemetrySharingStatus
|
||||
class FakeTelemetry extends SessionTelemetryBackend {
|
||||
override readonly sharing: SessionTelemetrySharingStatus
|
||||
|
||||
constructor(ctx: Context, config: { sharing: TelemetrySharingStatus }) {
|
||||
constructor(ctx: Context, config: { sharing: SessionTelemetrySharingStatus }) {
|
||||
super(ctx)
|
||||
this.sharing = config.sharing
|
||||
}
|
||||
@@ -68,9 +68,9 @@ function stubAgent(ctx: Context, id: string): { agent: Agent; session: Session }
|
||||
* backend disclosing one sharing policy. Without `sharing`, no telemetry
|
||||
* service exists and the acknowledgement reports "not configured".
|
||||
*/
|
||||
async function harness(sharing?: TelemetrySharingStatus): Promise<Harness> {
|
||||
async function harness(sharing?: SessionTelemetrySharingStatus): Promise<Harness> {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(CommandService)
|
||||
await ctx.plugin(CommandRuntime)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.plugin(SessionStore)
|
||||
if (sharing !== undefined) await ctx.plugin(FakeTelemetry, { sharing })
|
||||
@@ -124,7 +124,7 @@ describe('/feedback human command', () => {
|
||||
const test = await harness()
|
||||
await expect(run(test, ' the diff view is unreadable')).resolves.toEqual({
|
||||
kind: 'success',
|
||||
text: `Feedback recorded for session ${test.session.id}\nUser: ${USER_ID}. Session sharing is not configured.`,
|
||||
text: `Feedback recorded for session ${test.session.id}\nAnonymous user: ${USER_ID}. Session sharing is not configured.`,
|
||||
})
|
||||
expect(feedbackTexts(test.session)).toEqual(['the diff view is unreadable'])
|
||||
const commandRun = test.session.events.find(event => event.type === 'command/run')
|
||||
@@ -172,8 +172,8 @@ describe('/feedback human command', () => {
|
||||
test.ctx.commands.execute(test.agent, '/feedback second', signal),
|
||||
])
|
||||
expect(settled.map(item => item?.result)).toEqual([
|
||||
{ kind: 'success', text: `Feedback recorded for session ${test.session.id}\nUser: ${USER_ID}. Session sharing is not configured.` },
|
||||
{ kind: 'success', text: `Feedback recorded for session ${test.session.id}\nUser: ${USER_ID}. Session sharing is not configured.` },
|
||||
{ kind: 'success', text: `Feedback recorded for session ${test.session.id}\nAnonymous user: ${USER_ID}. Session sharing is not configured.` },
|
||||
{ kind: 'success', text: `Feedback recorded for session ${test.session.id}\nAnonymous user: ${USER_ID}. Session sharing is not configured.` },
|
||||
])
|
||||
expect(feedbackTexts(test.session)).toEqual(['first', 'second'])
|
||||
})
|
||||
@@ -182,7 +182,7 @@ describe('/feedback human command', () => {
|
||||
const test = await harness('full')
|
||||
await expect(run(test, ' everything shared')).resolves.toEqual({
|
||||
kind: 'success',
|
||||
text: `Feedback recorded for session ${test.session.id}\nUser: ${USER_ID}. Session sharing is enabled.`,
|
||||
text: `Feedback recorded for session ${test.session.id}\nAnonymous user: ${USER_ID}. Session sharing is enabled.`,
|
||||
})
|
||||
expect(feedbackTexts(test.session)).toEqual(['everything shared'])
|
||||
})
|
||||
@@ -191,7 +191,7 @@ describe('/feedback human command', () => {
|
||||
const test = await harness('feedback-only')
|
||||
await expect(run(test, ' gated sharing')).resolves.toEqual({
|
||||
kind: 'success',
|
||||
text: `Feedback recorded for session ${test.session.id}\nUser: ${USER_ID}. Session sharing is feedback-gated; recording feedback releases the session prefix for sharing.`,
|
||||
text: `Feedback recorded for session ${test.session.id}\nAnonymous user: ${USER_ID}. Session sharing is feedback-gated; recording feedback releases the session prefix for sharing.`,
|
||||
})
|
||||
expect(feedbackTexts(test.session)).toEqual(['gated sharing'])
|
||||
})
|
||||
@@ -200,7 +200,7 @@ describe('/feedback human command', () => {
|
||||
const test = await harness('disabled')
|
||||
await expect(run(test, ' local only')).resolves.toEqual({
|
||||
kind: 'success',
|
||||
text: `Feedback recorded for session ${test.session.id}\nUser: ${USER_ID}. Session sharing is disabled.`,
|
||||
text: `Feedback recorded for session ${test.session.id}\nAnonymous user: ${USER_ID}. Session sharing is disabled.`,
|
||||
})
|
||||
expect(feedbackTexts(test.session)).toEqual(['local only'])
|
||||
})
|
||||
|
||||
@@ -8,10 +8,10 @@ import Loader from '@deepseek-ai/cordis-plugin-loader'
|
||||
import Include from '@deepseek-ai/cordis-plugin-include'
|
||||
import AgentRegistry, { Inbox } from '@deepseek-ai/dsh-agent'
|
||||
import type { Agent, AgentStatus } from '@deepseek-ai/dsh-agent'
|
||||
import CommandService from '@deepseek-ai/dsh-commands'
|
||||
import CommandRuntime from '@deepseek-ai/dsh-commands'
|
||||
import SessionStore, { SessionId } from '@deepseek-ai/dsh-session'
|
||||
import * as CommandFeedback from '@deepseek-ai/dsh-command-feedback'
|
||||
import { getOrCreateAnonymousUserId } from '@deepseek-ai/dsh-user-id'
|
||||
import { getOrCreateAnonymousUserId } from '@deepseek-ai/dsh-anonymous-user-id'
|
||||
|
||||
let root: string | undefined
|
||||
let context: Context | undefined
|
||||
@@ -70,7 +70,7 @@ describe('/feedback real Loader composition through cordis.yml', () => {
|
||||
const modules = new Map<string, unknown>([
|
||||
['@deepseek-ai/dsh-agent', AgentRegistry],
|
||||
['@deepseek-ai/dsh-session', SessionStore],
|
||||
['@deepseek-ai/dsh-commands', CommandService],
|
||||
['@deepseek-ai/dsh-commands', CommandRuntime],
|
||||
['@deepseek-ai/dsh-command-feedback', CommandFeedback],
|
||||
])
|
||||
context.loader.internal = {
|
||||
@@ -93,7 +93,7 @@ describe('/feedback real Loader composition through cordis.yml', () => {
|
||||
const userId = getOrCreateAnonymousUserId({ env: { DSH_HOME: root } })
|
||||
expect(accepted?.result).toEqual({
|
||||
kind: 'success',
|
||||
text: `Feedback recorded for session feedback-loader-agent\nUser: ${userId}. Session sharing is not configured.`,
|
||||
text: `Feedback recorded for session feedback-loader-agent\nAnonymous user: ${userId}. Session sharing is not configured.`,
|
||||
})
|
||||
const rejected = await context.commands.execute(owner, '/feedback', signal)
|
||||
expect(rejected?.result).toEqual({
|
||||
|
||||
@@ -21,13 +21,13 @@
|
||||
"path": "../../core/session"
|
||||
},
|
||||
{
|
||||
"path": "../../session/user-id"
|
||||
"path": "../../identity/anonymous-user-id"
|
||||
},
|
||||
{
|
||||
"path": "../../session/session-telemetry"
|
||||
},
|
||||
{
|
||||
"path": "../../support/invariants"
|
||||
"path": "../../runtime-diagnostics/invariants"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write packages/feedback/message-feedback/README.md
|
||||
README.md: 54c6b92fbb68027a4aedb198948785183661a724
|
||||
README.zh.md: 376d161559fb93f855eb2a5341901d53b5e99f18
|
||||
README.md: b85104abb983eb3b33ab6ddad9328e6f5f3b1fc9
|
||||
README.zh.md: 52efef5119854f4a5c3c776a227032b59b680fc5
|
||||
|
||||
@@ -37,7 +37,7 @@ Message feedback is not Session-log content or a Session projection. It emits no
|
||||
|
||||
## Service and Host Remote contract
|
||||
|
||||
The same three `MessageFeedbackService` methods are published by `GatewayService` and `@Remote`; the Host endpoint names are `messageFeedback.list`, `messageFeedback.put`, and `messageFeedback.delete`. Every method returns a discriminated business union: `{ ok: true, value }` or `{ ok: false, error }`. Operational storage, corruption, or missing-durability-listener failures reject instead of being mislabeled as business errors.
|
||||
The same three `MessageFeedbackService` methods are published by `TypertRemoteService` and `@Remote`; the Host endpoint names are `messageFeedback.list`, `messageFeedback.put`, and `messageFeedback.delete`. Every method returns a discriminated business union: `{ ok: true, value }` or `{ ok: false, error }`. Operational storage, corruption, or missing-durability-listener failures reject instead of being mislabeled as business errors.
|
||||
|
||||
| Method | Request | Success `value` | Rejected `error.code` |
|
||||
|---|---|---|---|
|
||||
|
||||
@@ -37,7 +37,7 @@ message feedback 不是 Session 日志内容或 Session 投影。它不发出 `f
|
||||
|
||||
## 服务与 Host Remote 契约
|
||||
|
||||
`GatewayService` 与 `@Remote` 将 `MessageFeedbackService` 的同三个方法发布出去;Host endpoint 名称为 `messageFeedback.list`、`messageFeedback.put` 与 `messageFeedback.delete`。每个方法都返回判别式业务 union:`{ ok: true, value }` 或 `{ ok: false, error }`。存储、损坏或缺少 durability listener 等操作故障会产生 reject,不会被误标为业务错误。
|
||||
`TypertRemoteService` 与 `@Remote` 将 `MessageFeedbackService` 的同三个方法发布出去;Host endpoint 名称为 `messageFeedback.list`、`messageFeedback.put` 与 `messageFeedback.delete`。每个方法都返回判别式业务 union:`{ ok: true, value }` 或 `{ ok: false, error }`。存储、损坏或缺少 durability listener 等操作故障会产生 reject,不会被误标为业务错误。
|
||||
|
||||
| 方法 | 请求 | 成功 `value` | 拒绝的 `error.code` |
|
||||
|---|---|---|---|
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
"@deepseek-ai/dsh-session": "workspace:^",
|
||||
"@deepseek-ai/dsh-session-persistence": "workspace:^",
|
||||
"@deepseek-ai/dsh-storage-domain": "workspace:^",
|
||||
"@deepseek-ai/dsh-type-meta": "workspace:^",
|
||||
"@deepseek-ai/dsh-typert-protocol": "workspace:^",
|
||||
"@deepseek-ai/cordis": "workspace:^"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -74,7 +74,7 @@
|
||||
"@deepseek-ai/dsh-storage": "workspace:^",
|
||||
"@deepseek-ai/dsh-storage-domain": "workspace:^",
|
||||
"@deepseek-ai/dsh-storage-json": "workspace:^",
|
||||
"@deepseek-ai/dsh-type-meta": "workspace:^",
|
||||
"@deepseek-ai/dsh-typert-protocol": "workspace:^",
|
||||
"@deepseek-ai/cordis": "workspace:^"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import { deriveEventMessage, isAppendSurfaceEvent } from '@deepseek-ai/dsh-sessi
|
||||
import type { SessionHeader, SessionId } from '@deepseek-ai/dsh-session/types'
|
||||
import type { SessionInspection } from '@deepseek-ai/dsh-session-persistence'
|
||||
import type { KvTable } from '@deepseek-ai/dsh-storage-domain'
|
||||
import { GatewayService, Remote } from '@deepseek-ai/dsh-type-meta'
|
||||
import { TypertRemoteService, Remote } from '@deepseek-ai/dsh-typert-protocol'
|
||||
import { messageFeedbackDomainSpec } from './spec.ts'
|
||||
import type { MessageFeedbackRow, MessageFeedbackSessionIdentity } from './spec.ts'
|
||||
import type {
|
||||
@@ -147,7 +147,7 @@ type ResolvedNote =
|
||||
* Storage-domain sidecar service. It inspects persisted Session history and
|
||||
* never creates or resumes an Agent or Session.
|
||||
*/
|
||||
export class MessageFeedbackService extends GatewayService {
|
||||
export class MessageFeedbackService extends TypertRemoteService {
|
||||
static inject = ['storageDomain', 'sessionPersistence', 'sessions']
|
||||
|
||||
/** Loader validation for the required note-size policy. */
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import InvariantService from '@deepseek-ai/dsh-invariants'
|
||||
import InvariantRegistry from '@deepseek-ai/dsh-invariants'
|
||||
import * as MessageFeedbackInvariant from '../src/invariant.ts'
|
||||
import { setupHarness } from './helpers.ts'
|
||||
|
||||
@@ -7,7 +7,7 @@ describe('message-feedback invariant companion', () => {
|
||||
it('removes its registry contribution when its fiber is disposed (HMR safety)', async () => {
|
||||
const harness = await setupHarness()
|
||||
try {
|
||||
await harness.ctx.plugin(InvariantService)
|
||||
await harness.ctx.plugin(InvariantRegistry)
|
||||
const fiber = await harness.ctx.plugin(MessageFeedbackInvariant)
|
||||
|
||||
expect(() => {
|
||||
|
||||
@@ -7,11 +7,11 @@ import { Context } from '@deepseek-ai/cordis'
|
||||
import Include from '@deepseek-ai/cordis-plugin-include'
|
||||
import Loader from '@deepseek-ai/cordis-plugin-loader'
|
||||
import SessionStore, { SessionId } from '@deepseek-ai/dsh-session'
|
||||
import SessionPersistenceJsonl from '@deepseek-ai/dsh-session-persistence-jsonl'
|
||||
import JsonlSessionPersistence from '@deepseek-ai/dsh-session-persistence-jsonl'
|
||||
import Storage from '@deepseek-ai/dsh-storage'
|
||||
import * as StorageDomain from '@deepseek-ai/dsh-storage-domain'
|
||||
import * as StorageJson from '@deepseek-ai/dsh-storage-json'
|
||||
import { remoteMethods } from '@deepseek-ai/dsh-type-meta'
|
||||
import { remoteMethods } from '@deepseek-ai/dsh-typert-protocol'
|
||||
import MessageFeedbackService from '../src/index.ts'
|
||||
import { appendMessageFixture } from './helpers.ts'
|
||||
|
||||
@@ -31,7 +31,7 @@ async function loadComposition(configPath: string): Promise<Context> {
|
||||
ctx.loader.builtins.include = Include
|
||||
const modules = new Map<string, unknown>([
|
||||
['@deepseek-ai/dsh-session', SessionStore],
|
||||
['@deepseek-ai/dsh-session-persistence-jsonl', SessionPersistenceJsonl],
|
||||
['@deepseek-ai/dsh-session-persistence-jsonl', JsonlSessionPersistence],
|
||||
['@deepseek-ai/dsh-storage', Storage],
|
||||
['@deepseek-ai/dsh-storage-json', StorageJson],
|
||||
['@deepseek-ai/dsh-storage-domain', StorageDomain],
|
||||
@@ -81,7 +81,7 @@ describe('message feedback through a real Loader composition', () => {
|
||||
].join('\n'))
|
||||
|
||||
const first = await loadComposition(configPath)
|
||||
expect(first.messageFeedback.typertGateway.namespace).toBe('messageFeedback')
|
||||
expect(first.messageFeedback.typertRemote.namespace).toBe('messageFeedback')
|
||||
expect(remoteMethods(first.messageFeedback).map(marker => marker.method))
|
||||
.toEqual(['list', 'put', 'delete'])
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import type { MessageId } from '@deepseek-ai/dsh-llm/brand'
|
||||
import { Session, SessionId } from '@deepseek-ai/dsh-session'
|
||||
import { remoteMethods } from '@deepseek-ai/dsh-type-meta'
|
||||
import { remoteMethods } from '@deepseek-ai/dsh-typert-protocol'
|
||||
import MessageFeedbackService, { messageFeedbackRowSchema } from '../src/index.ts'
|
||||
import type {
|
||||
MessageFeedbackItem,
|
||||
@@ -43,7 +43,7 @@ function expectItem(
|
||||
describe('MessageFeedbackService public contract', () => {
|
||||
it('publishes the exact Gateway namespace and Remote method names', async () => {
|
||||
const { ctx } = await harness()
|
||||
const binding = ctx.messageFeedback.typertGateway
|
||||
const binding = ctx.messageFeedback.typertRemote
|
||||
expect(binding.serviceKey).toBe('messageFeedback')
|
||||
expect(binding.namespace).toBe('messageFeedback')
|
||||
expect(remoteMethods(ctx.messageFeedback)).toEqual([
|
||||
|
||||
@@ -36,10 +36,10 @@
|
||||
"path": "../../storage/storage-domain"
|
||||
},
|
||||
{
|
||||
"path": "../../typert/type-meta"
|
||||
"path": "../../typert/protocol"
|
||||
},
|
||||
{
|
||||
"path": "../../support/invariants"
|
||||
"path": "../../runtime-diagnostics/invariants"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user