Merge master into DSH 0.2.1-rc.3 synchronization branch

This commit is contained in:
imccyu committed 2026-10-23 13:38:11 +08:00
commit 8a01b8360b
254 files changed
+16147 -252

No files matched your search

+3 -1
View File
@@ -107,7 +107,9 @@
"@deepseek-ai/dsh-experimental-session-search": "workspace:*",
"@deepseek-ai/dsh-experimental-tool-worktree": "workspace:*",
"@deepseek-ai/dsh-cua-bundle": "workspace:*",
"@deepseek-ai/dsh-experimental-plugin-market-bundle": "workspace:*"
"@deepseek-ai/dsh-experimental-plugin-market-bundle": "workspace:*",
"@deepseek-ai/dsh-creation-recommendations": "workspace:*",
"@deepseek-ai/dsh-plugin-recommendations": "workspace:*"
},
"devDependencies": {
"@deepseek-ai/dsh-skill-badge": "workspace:*",
@@ -0,0 +1,130 @@
/** The completed answer offers creation through a propose_action card; choosing a location does not start it. */
import { readFile } from 'node:fs/promises'
import { join } from 'node:path'
import { fileURLToPath } from 'node:url'
import { chromium, type Browser, type Page } from 'playwright'
import { afterAll, beforeAll, describe, expect, it } from 'vitest'
import {
acknowledgeReloadConnectionLoss, assertFixtureInventory, captureStableAria, compareOrRefreshGolden,
fixtureUserPrompts, launchWebScaffold, watchConsole, type WebScaffold,
} from './scaffold.ts'
import { connectFreshWorkspace, newEnglishPage } from './support.ts'
const directory = fileURLToPath(new URL('../../../snapshots/web/creation-recommendations', import.meta.url))
const fixture = join(directory, 'session.v4.jsonl')
describe('creation recommendation after an explicit wish', () => {
let scaffold: WebScaffold
let browser: Browser
let page: Page
beforeAll(async () => {
scaffold = await launchWebScaffold({ replayFixture: fixture, compareReplaySession: true,
profile: { packages: [{ dir: fileURLToPath(new URL('../../../packages/experimental/creation-recommendations', import.meta.url)), enabled: true }] },
})
browser = await chromium.launch()
page = await newEnglishPage(browser)
})
afterAll(async () => { await browser?.close(); await scaffold?.close() })
it('shows one card below the answer, lets the user change location, and restores a dismissal', async () => {
const tripwire = watchConsole(page)
await page.setViewportSize({ width: 620, height: 900 })
await page.goto(scaffold.authenticatedUrl, { waitUntil: 'load' })
await connectFreshWorkspace(page, scaffold.workspaceCwd)
const input = page.locator('[data-composer-input]').first()
const settled = scaffold.whenTurnSettled()
await input.fill(fixtureUserPrompts(await readFile(fixture, 'utf8'))[0]!)
await input.press('Enter')
await settled
const card = page.locator('[data-action-card]')
await card.waitFor({ state: 'visible' })
expect(await card.count()).toBe(1)
expect(await page.locator('[data-turn-tail] [data-action-card]').count()).toBe(1)
const answer = page.getByText('I can create a persistent counter with increment and reset controls. Choose its location below.', { exact: true })
const answerBox = await answer.boundingBox()
const cardBox = await card.boundingBox()
expect(answerBox).not.toBeNull()
expect(cardBox!.y).toBeGreaterThanOrEqual(answerBox!.y + answerBox!.height)
expect(await card.getByRole('button', { name: 'Create', exact: true }).isEnabled()).toBe(true)
await compareOrRefreshGolden(join(directory, 'offered.expected.md'), await captureStableAria(page, '[data-action-card]', scaffold.workspaceCwd), scaffold.mode)
const info = card.getByRole('button', { name: 'How creation works and token usage', exact: true })
expect(await info.getAttribute('aria-expanded')).toBe('true')
expect(await card.getByRole('note').innerText()).toContain('large number of tokens')
await info.focus()
await info.press('Space')
expect(await card.getByRole('note').count()).toBe(0)
const infoWarnings = tripwire.warnings.length
await page.reload({ waitUntil: 'load' })
await card.waitFor({ state: 'visible' })
acknowledgeReloadConnectionLoss(tripwire, infoWarnings)
expect(await info.getAttribute('aria-expanded')).toBe('false')
await info.focus()
await info.press('Enter')
expect(await info.getAttribute('aria-expanded')).toBe('true')
expect(await card.getByRole('note').innerText()).toContain('large number of tokens')
expect(await card.getByRole('note').innerText()).toContain('keep chatting here')
expect(await card.getByRole('button', { name: 'Create', exact: true }).isEnabled()).toBe(true)
expect(await card.evaluate(element => element.scrollWidth <= element.clientWidth)).toBe(true)
await compareOrRefreshGolden(join(directory, 'info.expected.md'), await captureStableAria(page, '[data-action-card]', scaffold.workspaceCwd), scaffold.mode)
await info.press('Space')
expect(await info.getAttribute('aria-expanded')).toBe('false')
expect(await card.getByRole('note').count()).toBe(0)
const expand = card.getByRole('button', { name: 'Show more', exact: true })
const plan = page.locator(`[id="${await expand.getAttribute('aria-controls')}"]`)
const compactHeight = await plan.evaluate(element => element.clientHeight)
const previewText = await plan.innerText()
expect(previewText.endsWith('…')).toBe(true)
// The preview keeps about 260 Latin-letter widths before the ellipsis; a trailing space may be trimmed.
const previewLength = Array.from(new Intl.Segmenter(undefined, { granularity: 'grapheme' }).segment(previewText)).length
expect(previewLength).toBeGreaterThan(200)
expect(previewLength).toBeLessThanOrEqual(261)
await expand.click()
expect(await plan.evaluate(element => element.clientHeight)).toBeGreaterThan(compactHeight)
expect(await card.getByRole('button', { name: 'Create', exact: true }).isEnabled()).toBe(true)
await compareOrRefreshGolden(join(directory, 'expanded.expected.md'), await captureStableAria(page, '[data-action-card]', scaffold.workspaceCwd), scaffold.mode)
await card.getByRole('button', { name: 'Show less', exact: true }).click()
const location = card.getByRole('button', { name: 'Add to: Right sidebar', exact: true })
await location.click()
expect(await page.getByRole('menuitem').count()).toBe(4)
await compareOrRefreshGolden(join(directory, 'location-menu.expected.md'), await captureStableAria(page, '[role="menu"]', scaffold.workspaceCwd), scaffold.mode)
await page.getByRole('menuitem').first().press('Escape')
expect(await page.getByRole('menu').count()).toBe(0)
expect(await location.evaluate(element => element === document.activeElement)).toBe(true)
await location.click()
await page.getByRole('menuitem', { name: /Write your own/ }).click()
const custom = card.getByRole('textbox', { name: 'Where should it appear?', exact: true })
expect(await card.getByRole('button', { name: 'Create', exact: true }).isEnabled()).toBe(false)
await custom.fill('A left sidebar entry that opens a full-page calendar')
expect(await card.getByRole('button', { name: 'Create', exact: true }).isEnabled()).toBe(true)
await compareOrRefreshGolden(join(directory, 'custom-location.expected.md'), await captureStableAria(page, '[data-action-card]', scaffold.workspaceCwd), scaffold.mode)
await card.getByRole('button', { name: 'Add to: Write your own', exact: true }).click()
await page.getByRole('menuitem', { name: /Session tab/ }).click()
expect(await card.getByRole('button', { name: 'Add to: Session tab', exact: true }).isVisible()).toBe(true)
expect(await custom.count()).toBe(0)
await compareOrRefreshGolden(join(directory, 'options.expected.md'), await captureStableAria(page, '[data-action-card]', scaffold.workspaceCwd), scaffold.mode)
expect(await card.evaluate(element => element.scrollWidth <= element.clientWidth)).toBe(true)
await card.getByRole('button', { name: 'Not now', exact: true }).click()
await card.getByRole('status').getByText('Dismissed', { exact: true }).waitFor()
const warnings = tripwire.warnings.length
await page.reload({ waitUntil: 'load' })
await card.getByRole('status').getByText('Dismissed', { exact: true }).waitFor()
acknowledgeReloadConnectionLoss(tripwire, warnings)
expect(await card.getByRole('button', { name: 'Create', exact: true }).count()).toBe(0)
expect(await card.getByRole('button', { name: 'Show more', exact: true }).isVisible()).toBe(true)
expect((await scaffold.ctx.pluginManager.listBundles()).some(bundle => bundle.name.startsWith('@dsh-local/view-'))).toBe(false)
await compareOrRefreshGolden(join(directory, 'dismissed.expected.md'), await captureStableAria(page, '[data-action-card]', scaffold.workspaceCwd), scaffold.mode)
const statusSettled = scaffold.whenTurnSettled()
await input.fill(fixtureUserPrompts(await readFile(fixture, 'utf8'))[1]!)
await input.press('Enter')
await statusSettled
await page.getByText('The counter proposal was dismissed. Creation never started.', { exact: true }).waitFor()
expect(await card.count()).toBe(1)
expect(await card.getByRole('button', { name: 'Create', exact: true }).count()).toBe(0)
expect(tripwire.pageErrors).toEqual([])
expect(tripwire.warnings).toEqual([])
if (scaffold.mode === 'replay') await assertFixtureInventory(directory, [
'session.v4.jsonl', 'offered.expected.md', 'info.expected.md', 'expanded.expected.md', 'options.expected.md', 'dismissed.expected.md', 'location-menu.expected.md', 'custom-location.expected.md', 'system-prompt.expected.md', 'tool-schemas.expected.json',
])
})
})
+2
View File
@@ -863,6 +863,8 @@ describe('web e2e: plugin manager', () => {
['@deepseek-ai/dsh-experimental-voice-input-bundle', 1],
['@deepseek-ai/dsh-experimental-plugin-market-bundle', 1],
['@deepseek-ai/dsh-experimental-auto-review', 1],
['@deepseek-ai/dsh-creation-recommendations', 1],
['@deepseek-ai/dsh-plugin-recommendations', 1],
['@deepseek-ai/dsh-experimental-inspector-profile', 1],
['@deepseek-ai/dsh-cua-bundle', 1],
['@deepseek-ai/dsh-experimental-tool-worktree', 1],
@@ -0,0 +1,87 @@
/** Replay model output through the shipped Web profile and decide its real propose_action card. */
import { readFile } from 'node:fs/promises'
import { join } from 'node:path'
import { fileURLToPath } from 'node:url'
import { chromium, type Browser, type Page } from 'playwright'
import { afterAll, beforeAll, describe, expect, it } from 'vitest'
import type {} from '@deepseek-ai/dsh-plugin-manager'
import {
acknowledgeReloadConnectionLoss, assertFixtureInventory, captureStableAria, compareOrRefreshGolden,
fixtureUserPrompts, launchWebScaffold, watchConsole, type WebScaffold,
} from './scaffold.ts'
import { connectFreshWorkspace, newEnglishPage } from './support.ts'
const directory = fileURLToPath(new URL('../../../snapshots/web/plugin-recommendations', import.meta.url))
const fixture = join(directory, 'session.v4.jsonl')
const voiceBundle = '@deepseek-ai/dsh-experimental-voice-input-bundle'
describe('official plugin recommendation in a conversation', () => {
let scaffold: WebScaffold
let browser: Browser
let page: Page
beforeAll(async () => {
scaffold = await launchWebScaffold({ replayFixture: fixture, compareReplaySession: true,
profile: { packages: [{ dir: fileURLToPath(new URL('../../../packages/experimental/plugin-recommendations', import.meta.url)), enabled: true }] },
})
browser = await chromium.launch()
page = await newEnglishPage(browser)
})
afterAll(async () => {
await browser?.close()
await scaffold?.close()
})
it('offers the propose_action call as one card below the answer, and reports a dismissal on the next request', async () => {
const tripwire = watchConsole(page)
await page.goto(scaffold.authenticatedUrl, { waitUntil: 'load' })
await connectFreshWorkspace(page, scaffold.workspaceCwd)
const input = page.locator('[data-composer-input]').first()
const settled = scaffold.whenTurnSettled()
const prompts = fixtureUserPrompts(await readFile(fixture, 'utf8'))
await input.fill(prompts[0]!)
await input.press('Enter')
await settled
const card = page.locator('[data-action-card]')
await card.waitFor({ state: 'visible' })
expect(await card.count()).toBe(1)
expect(await page.locator('[data-turn-tail] [data-action-card][data-action="enable_official_plugin"]').count()).toBe(1)
const answer = page.getByText('Voice input transcribes your recordings locally into the composer. You can enable it below.', { exact: true })
const answerBox = await answer.boundingBox()
const cardBox = await card.boundingBox()
expect(answerBox).not.toBeNull()
expect(cardBox!.y).toBeGreaterThanOrEqual(answerBox!.y + answerBox!.height)
expect(await page.getByText('enable_official_plugin').count()).toBe(0)
await card.getByText('Voice input', { exact: true }).waitFor()
expect(await card.getByRole('button', { name: 'Enable', exact: true }).isEnabled()).toBe(true)
expect((await scaffold.ctx.pluginManager.listBundles()).find(bundle => bundle.name === voiceBundle)?.enabled).toBe(false)
await compareOrRefreshGolden(join(directory, 'offered.expected.md'), await captureStableAria(page, '[data-action-card]', scaffold.workspaceCwd), scaffold.mode)
await page.setViewportSize({ width: 620, height: 900 })
expect(await card.evaluate(element => element.scrollWidth <= element.clientWidth)).toBe(true)
await card.getByRole('button', { name: 'Not now', exact: true }).click()
await card.getByRole('status').getByText('Dismissed', { exact: true }).waitFor()
const warnings = tripwire.warnings.length
await page.reload({ waitUntil: 'load' })
await card.getByRole('status').getByText('Dismissed', { exact: true }).waitFor()
acknowledgeReloadConnectionLoss(tripwire, warnings)
expect(await card.count()).toBe(1)
expect(await card.getByRole('button').count()).toBe(1)
expect((await scaffold.ctx.pluginManager.listBundles()).find(bundle => bundle.name === voiceBundle)?.enabled).toBe(false)
await compareOrRefreshGolden(join(directory, 'dismissed.expected.md'), await captureStableAria(page, '[data-action-card]', scaffold.workspaceCwd), scaffold.mode)
const statusSettled = scaffold.whenTurnSettled()
await input.fill(prompts[1]!)
await input.press('Enter')
await statusSettled
await page.getByText('Voice input is not enabled: you dismissed the card.', { exact: true }).waitFor()
expect(await card.count()).toBe(1)
await card.getByRole('button', { name: 'View details: Voice input', exact: true }).click()
await page.locator(`[data-plugin-detail="${voiceBundle}"]`).waitFor({ state: 'visible' })
expect((await scaffold.ctx.pluginManager.listBundles()).find(bundle => bundle.name === voiceBundle)?.enabled).toBe(false)
expect(tripwire.pageErrors).toEqual([])
expect(tripwire.warnings).toEqual([])
if (scaffold.mode === 'replay') await assertFixtureInventory(directory, [
'session.v4.jsonl', 'offered.expected.md', 'dismissed.expected.md', 'system-prompt.expected.md', 'tool-schemas.expected.json',
])
})
})
+2
View File
@@ -23,6 +23,8 @@
// this client-registered project must not also hold them (one program
// cannot see both sides of the cordis Context merges).
"exclude": [
"tests/creation-recommendations.e2e.ts",
"tests/plugin-recommendations.e2e.ts",
"tests/japanese-plugin-metadata.e2e.ts",
"tests/additional-locales.e2e.ts",
"tests/rich-text-quotes.e2e.ts",
+2 -2
View File
@@ -6,5 +6,5 @@
en: 41d772192075e133
zh: 5ef3da69b4de1d8a
/capability-seams-and-core-services:
en: f2546ac3454d220d
zh: 642a473c0405e81f
en: 506d61dfa8c71186
zh: 46f05c4ef2f5c322
+14
View File
@@ -7,6 +7,13 @@ A service can be a core spine service, a swappable capability seam, a bundle/com
```mermaid
flowchart LR
pkg_action_cards["action-cards"]
svc_actionCards["ctx.actionCards<br/>Optional action cards"]
pkg_creation_recommendations["creation-recommendations"]
pkg_client_ui_action_cards["client-ui-action-cards"]
pkg_plugin_recommendations["plugin-recommendations"]
svc_pluginRecommendations["ctx.pluginRecommendations<br/>Explicit-need plugin recommendations"]
pkg_client_ui_plugin_recommendations["client-ui-plugin-recommendations"]
pkg_headless["headless"]
svc_headlessStartup["ctx.headlessStartup<br/>Headless invocation options"]
pkg_experimental_client_ui_cot_translation["experimental-client-ui-cot-translation"]
@@ -298,6 +305,7 @@ flowchart LR
pkg_cordis_host_runner["cordis-host-runner"]
svc_dynamicCordisRunner["ctx.dynamicCordisRunner<br/>Dynamic Cordis package host runner"]
svc_cordisInspect["ctx.cordisInspect<br/>Dynamic Cordis inspect registry"]
pkg_action_cards --> svc_actionCards
pkg_agent --> svc_agents
pkg_agent_default_model --> svc_agentDefaultModel
pkg_agent_loop --> svc_agentLoop
@@ -388,6 +396,7 @@ flowchart LR
pkg_plugin_manager --> svc_pluginManager
pkg_plugin_market --> svc_pluginMarket
pkg_plugin_package_inventory_deepseek --> svc_deepseekLlmApiExtensions
pkg_plugin_recommendations --> svc_pluginRecommendations
pkg_ptc_runtime --> svc_ptcRuntime
pkg_ptc_runtime_node --> svc_ptcRuntime
pkg_pwsh_local --> svc_shell
@@ -457,6 +466,8 @@ flowchart LR
pkg_working_directory --> svc_workingDirectory
pkg_workspace --> svc_workspaceRegistry
pkg_workspace_changes --> svc_workspaceChanges
svc_actionCards --> pkg_client_ui_action_cards
svc_actionCards --> pkg_creation_recommendations
svc_agentDefaultModel --> pkg_api_session_controller
svc_agentDefaultModel --> pkg_headless
svc_agentLoop --> pkg_base
@@ -515,6 +526,7 @@ flowchart LR
svc_pluginManager --> pkg_ui_settings_plugin_inventory
svc_pluginMarket --> pkg_client_ui_plugin_market
svc_pluginMarket --> pkg_plugin_market
svc_pluginRecommendations --> pkg_client_ui_plugin_recommendations
svc_pluginRegistryProbe --> pkg_client_ui_plugin_manager
svc_profileContext --> pkg_plugin_manager
svc_ptcRuntime --> pkg_tool_executor_ptc
@@ -619,6 +631,8 @@ flowchart LR
| ctx key | Role | Owner | Implementations | Direct consumers | Companion plugins | Note |
| --- | --- | --- | --- | --- | --- | --- |
| `ctx.actionCards` | `service` | [`action-cards`](../packages/experimental/action-cards) | - | [`creation-recommendations`](../packages/experimental/creation-recommendations), [`client-ui-action-cards`](../packages/experimental/client-ui-action-cards) | - | Owns the card tool, card state, user actions and status notices; card kinds register their actions and handlers. |
| `ctx.pluginRecommendations` | `service` | [`plugin-recommendations`](../packages/experimental/plugin-recommendations) | - | [`client-ui-plugin-recommendations`](../packages/experimental/client-ui-plugin-recommendations) | - | Reads the current catalog text of an official plugin card and enables the plugin through the profile plugin manager after a click. |
| `ctx.headlessStartup` | `core` | [`headless`](../packages/bundle/headless) | - | [`headless`](../packages/bundle/headless) | - | Supplies parsed task and run options to the headless runner and its sandbox-policy configuration. |
| `ctx.cotTranslation` | `service` | [`experimental-client-ui-cot-translation`](../packages/experimental/client-ui-cot-translation) | - | - | - | Serves reasoning translations, reuses saved results, and joins ordinary Session activation for uncached inactive Sessions. |
| `ctx.translator` | `service` | [`experimental-translator`](../packages/experimental/translator) | - | [`experimental-client-ui-cot-translation`](../packages/experimental/client-ui-cot-translation) | - | Resolves Google or Bing requests and retains shared experimental records through the active Session writer. |
+14
View File
@@ -9,6 +9,13 @@
```mermaid
flowchart LR
pkg_action_cards["action-cards"]
svc_actionCards["ctx.actionCards<br/>Optional action cards"]
pkg_creation_recommendations["creation-recommendations"]
pkg_client_ui_action_cards["client-ui-action-cards"]
pkg_plugin_recommendations["plugin-recommendations"]
svc_pluginRecommendations["ctx.pluginRecommendations<br/>Explicit-need plugin recommendations"]
pkg_client_ui_plugin_recommendations["client-ui-plugin-recommendations"]
pkg_headless["headless"]
svc_headlessStartup["ctx.headlessStartup<br/>Headless invocation options"]
pkg_experimental_client_ui_cot_translation["experimental-client-ui-cot-translation"]
@@ -300,6 +307,7 @@ flowchart LR
pkg_cordis_host_runner["cordis-host-runner"]
svc_dynamicCordisRunner["ctx.dynamicCordisRunner<br/>Dynamic Cordis package host runner"]
svc_cordisInspect["ctx.cordisInspect<br/>Dynamic Cordis inspect registry"]
pkg_action_cards --> svc_actionCards
pkg_agent --> svc_agents
pkg_agent_default_model --> svc_agentDefaultModel
pkg_agent_loop --> svc_agentLoop
@@ -390,6 +398,7 @@ flowchart LR
pkg_plugin_manager --> svc_pluginManager
pkg_plugin_market --> svc_pluginMarket
pkg_plugin_package_inventory_deepseek --> svc_deepseekLlmApiExtensions
pkg_plugin_recommendations --> svc_pluginRecommendations
pkg_ptc_runtime --> svc_ptcRuntime
pkg_ptc_runtime_node --> svc_ptcRuntime
pkg_pwsh_local --> svc_shell
@@ -459,6 +468,8 @@ flowchart LR
pkg_working_directory --> svc_workingDirectory
pkg_workspace --> svc_workspaceRegistry
pkg_workspace_changes --> svc_workspaceChanges
svc_actionCards --> pkg_client_ui_action_cards
svc_actionCards --> pkg_creation_recommendations
svc_agentDefaultModel --> pkg_api_session_controller
svc_agentDefaultModel --> pkg_headless
svc_agentLoop --> pkg_base
@@ -517,6 +528,7 @@ flowchart LR
svc_pluginManager --> pkg_ui_settings_plugin_inventory
svc_pluginMarket --> pkg_client_ui_plugin_market
svc_pluginMarket --> pkg_plugin_market
svc_pluginRecommendations --> pkg_client_ui_plugin_recommendations
svc_pluginRegistryProbe --> pkg_client_ui_plugin_manager
svc_profileContext --> pkg_plugin_manager
svc_ptcRuntime --> pkg_tool_executor_ptc
@@ -621,6 +633,8 @@ flowchart LR
| ctx 键 | 角色 | 所属包 | 实现 | 直接消费方 | 配套插件 | 说明 |
| --- | --- | --- | --- | --- | --- | --- |
| `ctx.actionCards` | `service` | [`action-cards`](../packages/experimental/action-cards) | - | [`creation-recommendations`](../packages/experimental/creation-recommendations), [`client-ui-action-cards`](../packages/experimental/client-ui-action-cards) | - | 负责卡片工具、卡片状态、用户操作和状态通知;各卡片种类注册自己的动作和处理器。 |
| `ctx.pluginRecommendations` | `service` | [`plugin-recommendations`](../packages/experimental/plugin-recommendations) | - | [`client-ui-plugin-recommendations`](../packages/experimental/client-ui-plugin-recommendations) | - | 读取官方插件卡片的当前目录文本,并在用户点击后通过配置插件管理器启用该插件。 |
| `ctx.headlessStartup` | `core` | [`headless`](../packages/bundle/headless) | - | [`headless`](../packages/bundle/headless) | - | 将解析后的任务和运行选项提供给无界面运行器及其 sandbox-policy 配置。 |
| `ctx.cotTranslation` | `service` | [`experimental-client-ui-cot-translation`](../packages/experimental/client-ui-cot-translation) | - | - | - | 为展开的思考内容提供有界的 Session 翻译请求,不改变模型可见历史。 |
| `ctx.translator` | `service` | [`experimental-translator`](../packages/experimental/translator) | - | [`experimental-client-ui-cot-translation`](../packages/experimental/client-ui-cot-translation) | - | 解析 Google 或 Bing 请求,并可选保留统一的实验性 Session 记录。 |
+20
View File
@@ -33,6 +33,21 @@ export interface AcpConfig {
```
<!-- END GENERATED config-catalog:@deepseek-ai/dsh-acp -->
<!-- BEGIN GENERATED config-catalog:@deepseek-ai/dsh-action-cards -->
<a id="deepseek-aidsh-action-cards"></a>
## `@deepseek-ai/dsh-action-cards`
- `inject`: `tools` · `agents` · `sessionProjections` · `remoteAccess`
- `refs`: `z` (`zod`)
- `source`: [`packages/experimental/action-cards/src/index.ts:23`](../packages/experimental/action-cards/src/index.ts)
```ts config-catalog
/** Validated card limits after schema defaults. */
export type Config = z.infer<typeof Config>
```
<!-- END GENERATED config-catalog:@deepseek-ai/dsh-action-cards -->
<!-- BEGIN GENERATED config-catalog:@deepseek-ai/dsh-agent-default-model -->
<a id="deepseek-aidsh-agent-default-model"></a>
@@ -4689,6 +4704,7 @@ These load from a `cordis.yml` entry with no `config:` block; they declare no co
| `@deepseek-ai/dsh-client-locale` | — | [`packages/client/locale/src/index.ts`](../packages/client/locale/src/index.ts) |
| `@deepseek-ai/dsh-client-modules` | `loader` | [`packages/client/modules/src/index.ts`](../packages/client/modules/src/index.ts) |
| `@deepseek-ai/dsh-client-resources` | — | [`packages/client/resources/src/index.ts`](../packages/client/resources/src/index.ts) |
| `@deepseek-ai/dsh-client-ui-action-cards` | — | [`packages/experimental/client-ui-action-cards/src/index.ts`](../packages/experimental/client-ui-action-cards/src/index.ts) |
| `@deepseek-ai/dsh-client-ui-agent-preset` | — | [`packages/client/ui-agent-preset/src/index.ts`](../packages/client/ui-agent-preset/src/index.ts) |
| `@deepseek-ai/dsh-client-ui-approval` | — | [`packages/client/ui-approval/src/index.ts`](../packages/client/ui-approval/src/index.ts) |
| `@deepseek-ai/dsh-client-ui-attachment` | — | [`packages/client/ui-attachment/src/index.ts`](../packages/client/ui-attachment/src/index.ts) |
@@ -4697,6 +4713,7 @@ These load from a `cordis.yml` entry with no `config:` block; they declare no co
| `@deepseek-ai/dsh-client-ui-commands` | — | [`packages/client/ui-commands/src/index.ts`](../packages/client/ui-commands/src/index.ts) |
| `@deepseek-ai/dsh-client-ui-conversation` | — | [`packages/client/ui-conversation/src/index.ts`](../packages/client/ui-conversation/src/index.ts) |
| `@deepseek-ai/dsh-client-ui-cordis` | — | [`packages/extensions/ui-cordis/src/index.ts`](../packages/extensions/ui-cordis/src/index.ts) |
| `@deepseek-ai/dsh-client-ui-creation-recommendations` | — | [`packages/experimental/client-ui-creation-recommendations/src/index.ts`](../packages/experimental/client-ui-creation-recommendations/src/index.ts) |
| `@deepseek-ai/dsh-client-ui-deepseek-model-label-1017` | — | [`packages/client/ui-deepseek-model-label-1017/src/index.ts`](../packages/client/ui-deepseek-model-label-1017/src/index.ts) |
| `@deepseek-ai/dsh-client-ui-deliverables` | `connection` · `sessionQuery` · `sessionController` · `workspaceFiles` · `fs` · `sandboxPolicy` · `workspaceChanges` | [`packages/client/ui-deliverables/src/index.ts`](../packages/client/ui-deliverables/src/index.ts) |
| `@deepseek-ai/dsh-client-ui-directory-picker-browse` | — | [`packages/client/ui-directory-picker-browse/src/index.ts`](../packages/client/ui-directory-picker-browse/src/index.ts) |
@@ -4713,6 +4730,7 @@ These load from a `cordis.yml` entry with no `config:` block; they declare no co
| `@deepseek-ai/dsh-client-ui-permission-presets` | — | [`packages/client/ui-permission-presets/src/index.ts`](../packages/client/ui-permission-presets/src/index.ts) |
| `@deepseek-ai/dsh-client-ui-plan` | — | [`packages/client/ui-plan/src/index.ts`](../packages/client/ui-plan/src/index.ts) |
| `@deepseek-ai/dsh-client-ui-plugin-market` | — | [`packages/client/ui-plugin-market/src/index.ts`](../packages/client/ui-plugin-market/src/index.ts) |
| `@deepseek-ai/dsh-client-ui-plugin-recommendations` | — | [`packages/experimental/client-ui-plugin-recommendations/src/index.ts`](../packages/experimental/client-ui-plugin-recommendations/src/index.ts) |
| `@deepseek-ai/dsh-client-ui-reference` | — | [`packages/client/ui-reference/src/index.ts`](../packages/client/ui-reference/src/index.ts) |
| `@deepseek-ai/dsh-client-ui-renderer` | — | [`packages/client/ui-renderer/src/index.ts`](../packages/client/ui-renderer/src/index.ts) |
| `@deepseek-ai/dsh-client-ui-schedule` | — | [`packages/client/ui-schedule/src/index.ts`](../packages/client/ui-schedule/src/index.ts) |
@@ -4748,6 +4766,7 @@ These load from a `cordis.yml` entry with no `config:` block; they declare no co
| `@deepseek-ai/dsh-computer-use` | — | [`packages/computer-use/computer-use/src/index.ts`](../packages/computer-use/computer-use/src/index.ts) |
| `@deepseek-ai/dsh-config-editor` | `loader` · `profileContext` | [`packages/boot/config-editor/src/index.ts`](../packages/boot/config-editor/src/index.ts) |
| `@deepseek-ai/dsh-cordis-client-runner` | — | [`packages/extensions/cordis-client-runner/src/index.ts`](../packages/extensions/cordis-client-runner/src/index.ts) |
| `@deepseek-ai/dsh-creation-recommendations` | `tools` · `pluginManager` · `agents` · `subagents` · `profileContext` · `skills` · `actionCards` | [`packages/experimental/creation-recommendations/src/index.ts`](../packages/experimental/creation-recommendations/src/index.ts) |
| `@deepseek-ai/dsh-cua` | — | [`packages/experimental/cua/src/index.ts`](../packages/experimental/cua/src/index.ts) |
| `@deepseek-ai/dsh-deepseek-llm-api-extensions` | — | [`packages/llm/deepseek-llm-api-extensions/src/index.ts`](../packages/llm/deepseek-llm-api-extensions/src/index.ts) |
| `@deepseek-ai/dsh-deliverable-file-references` | `systemPrompt` | [`packages/deliverables/deliverable-file-references/src/index.ts`](../packages/deliverables/deliverable-file-references/src/index.ts) |
@@ -4770,6 +4789,7 @@ These load from a `cordis.yml` entry with no `config:` block; they declare no co
| `@deepseek-ai/dsh-mcp-resources` | `tools` | [`packages/mcp/mcp-resources/src/index.ts`](../packages/mcp/mcp-resources/src/index.ts) |
| `@deepseek-ai/dsh-message-quotes` | — | [`packages/session/message-quotes/src/index.ts`](../packages/session/message-quotes/src/index.ts) |
| `@deepseek-ai/dsh-otel` | — | [`packages/telemetry/otel/src/index.ts`](../packages/telemetry/otel/src/index.ts) |
| `@deepseek-ai/dsh-plugin-recommendations` | `pluginManager` · `agents` · `skills` · `actionCards` | [`packages/experimental/plugin-recommendations/src/index.ts`](../packages/experimental/plugin-recommendations/src/index.ts) |
| `@deepseek-ai/dsh-sandbox-ssh` | `ssh` | [`packages/ssh/sandbox-ssh/src/index.ts`](../packages/ssh/sandbox-ssh/src/index.ts) |
| `@deepseek-ai/dsh-session` | — | [`packages/core/session/src/index.ts`](../packages/core/session/src/index.ts) |
| `@deepseek-ai/dsh-session-checkpoint-policy` | `llm` · `sessionPersistence` · `sessions` · `tools` | [`packages/session/session-checkpoint-policy/src/index.ts`](../packages/session/session-checkpoint-policy/src/index.ts) |
+20
View File
@@ -35,6 +35,21 @@ export interface AcpConfig {
```
<!-- END GENERATED config-catalog:@deepseek-ai/dsh-acp -->
<!-- BEGIN GENERATED config-catalog:@deepseek-ai/dsh-action-cards -->
<a id="deepseek-aidsh-action-cards"></a>
## `@deepseek-ai/dsh-action-cards`
- `inject`: `tools` · `agents` · `sessionProjections` · `remoteAccess`
- `refs`: `z` (`zod`)
- `source`: [`packages/experimental/action-cards/src/index.ts:23`](../packages/experimental/action-cards/src/index.ts)
```ts config-catalog
/** Validated card limits after schema defaults. */
export type Config = z.infer<typeof Config>
```
<!-- END GENERATED config-catalog:@deepseek-ai/dsh-action-cards -->
<!-- BEGIN GENERATED config-catalog:@deepseek-ai/dsh-agent-default-model -->
<a id="deepseek-aidsh-agent-default-model"></a>
@@ -4691,6 +4706,7 @@ export interface Config {
| `@deepseek-ai/dsh-client-locale` | — | [`packages/client/locale/src/index.ts`](../packages/client/locale/src/index.ts) |
| `@deepseek-ai/dsh-client-modules` | `loader` | [`packages/client/modules/src/index.ts`](../packages/client/modules/src/index.ts) |
| `@deepseek-ai/dsh-client-resources` | — | [`packages/client/resources/src/index.ts`](../packages/client/resources/src/index.ts) |
| `@deepseek-ai/dsh-client-ui-action-cards` | — | [`packages/experimental/client-ui-action-cards/src/index.ts`](../packages/experimental/client-ui-action-cards/src/index.ts) |
| `@deepseek-ai/dsh-client-ui-agent-preset` | — | [`packages/client/ui-agent-preset/src/index.ts`](../packages/client/ui-agent-preset/src/index.ts) |
| `@deepseek-ai/dsh-client-ui-approval` | — | [`packages/client/ui-approval/src/index.ts`](../packages/client/ui-approval/src/index.ts) |
| `@deepseek-ai/dsh-client-ui-attachment` | — | [`packages/client/ui-attachment/src/index.ts`](../packages/client/ui-attachment/src/index.ts) |
@@ -4699,6 +4715,7 @@ export interface Config {
| `@deepseek-ai/dsh-client-ui-commands` | — | [`packages/client/ui-commands/src/index.ts`](../packages/client/ui-commands/src/index.ts) |
| `@deepseek-ai/dsh-client-ui-conversation` | — | [`packages/client/ui-conversation/src/index.ts`](../packages/client/ui-conversation/src/index.ts) |
| `@deepseek-ai/dsh-client-ui-cordis` | — | [`packages/extensions/ui-cordis/src/index.ts`](../packages/extensions/ui-cordis/src/index.ts) |
| `@deepseek-ai/dsh-client-ui-creation-recommendations` | — | [`packages/experimental/client-ui-creation-recommendations/src/index.ts`](../packages/experimental/client-ui-creation-recommendations/src/index.ts) |
| `@deepseek-ai/dsh-client-ui-deepseek-model-label-1017` | — | [`packages/client/ui-deepseek-model-label-1017/src/index.ts`](../packages/client/ui-deepseek-model-label-1017/src/index.ts) |
| `@deepseek-ai/dsh-client-ui-deliverables` | `connection` · `sessionQuery` · `sessionController` · `workspaceFiles` · `fs` · `sandboxPolicy` · `workspaceChanges` | [`packages/client/ui-deliverables/src/index.ts`](../packages/client/ui-deliverables/src/index.ts) |
| `@deepseek-ai/dsh-client-ui-directory-picker-browse` | — | [`packages/client/ui-directory-picker-browse/src/index.ts`](../packages/client/ui-directory-picker-browse/src/index.ts) |
@@ -4715,6 +4732,7 @@ export interface Config {
| `@deepseek-ai/dsh-client-ui-permission-presets` | — | [`packages/client/ui-permission-presets/src/index.ts`](../packages/client/ui-permission-presets/src/index.ts) |
| `@deepseek-ai/dsh-client-ui-plan` | — | [`packages/client/ui-plan/src/index.ts`](../packages/client/ui-plan/src/index.ts) |
| `@deepseek-ai/dsh-client-ui-plugin-market` | — | [`packages/client/ui-plugin-market/src/index.ts`](../packages/client/ui-plugin-market/src/index.ts) |
| `@deepseek-ai/dsh-client-ui-plugin-recommendations` | — | [`packages/experimental/client-ui-plugin-recommendations/src/index.ts`](../packages/experimental/client-ui-plugin-recommendations/src/index.ts) |
| `@deepseek-ai/dsh-client-ui-reference` | — | [`packages/client/ui-reference/src/index.ts`](../packages/client/ui-reference/src/index.ts) |
| `@deepseek-ai/dsh-client-ui-renderer` | — | [`packages/client/ui-renderer/src/index.ts`](../packages/client/ui-renderer/src/index.ts) |
| `@deepseek-ai/dsh-client-ui-schedule` | — | [`packages/client/ui-schedule/src/index.ts`](../packages/client/ui-schedule/src/index.ts) |
@@ -4750,6 +4768,7 @@ export interface Config {
| `@deepseek-ai/dsh-computer-use` | — | [`packages/computer-use/computer-use/src/index.ts`](../packages/computer-use/computer-use/src/index.ts) |
| `@deepseek-ai/dsh-config-editor` | `loader` · `profileContext` | [`packages/boot/config-editor/src/index.ts`](../packages/boot/config-editor/src/index.ts) |
| `@deepseek-ai/dsh-cordis-client-runner` | — | [`packages/extensions/cordis-client-runner/src/index.ts`](../packages/extensions/cordis-client-runner/src/index.ts) |
| `@deepseek-ai/dsh-creation-recommendations` | `tools` · `pluginManager` · `agents` · `subagents` · `profileContext` · `skills` · `actionCards` | [`packages/experimental/creation-recommendations/src/index.ts`](../packages/experimental/creation-recommendations/src/index.ts) |
| `@deepseek-ai/dsh-cua` | — | [`packages/experimental/cua/src/index.ts`](../packages/experimental/cua/src/index.ts) |
| `@deepseek-ai/dsh-deepseek-llm-api-extensions` | — | [`packages/llm/deepseek-llm-api-extensions/src/index.ts`](../packages/llm/deepseek-llm-api-extensions/src/index.ts) |
| `@deepseek-ai/dsh-deliverable-file-references` | `systemPrompt` | [`packages/deliverables/deliverable-file-references/src/index.ts`](../packages/deliverables/deliverable-file-references/src/index.ts) |
@@ -4772,6 +4791,7 @@ export interface Config {
| `@deepseek-ai/dsh-mcp-resources` | `tools` | [`packages/mcp/mcp-resources/src/index.ts`](../packages/mcp/mcp-resources/src/index.ts) |
| `@deepseek-ai/dsh-message-quotes` | — | [`packages/session/message-quotes/src/index.ts`](../packages/session/message-quotes/src/index.ts) |
| `@deepseek-ai/dsh-otel` | — | [`packages/telemetry/otel/src/index.ts`](../packages/telemetry/otel/src/index.ts) |
| `@deepseek-ai/dsh-plugin-recommendations` | `pluginManager` · `agents` · `skills` · `actionCards` | [`packages/experimental/plugin-recommendations/src/index.ts`](../packages/experimental/plugin-recommendations/src/index.ts) |
| `@deepseek-ai/dsh-sandbox-ssh` | `ssh` | [`packages/ssh/sandbox-ssh/src/index.ts`](../packages/ssh/sandbox-ssh/src/index.ts) |
| `@deepseek-ai/dsh-session` | — | [`packages/core/session/src/index.ts`](../packages/core/session/src/index.ts) |
| `@deepseek-ai/dsh-session-checkpoint-policy` | `llm` · `sessionPersistence` · `sessions` · `tools` | [`packages/session/session-checkpoint-policy/src/index.ts`](../packages/session/session-checkpoint-policy/src/index.ts) |
+4 -4
View File
@@ -11,13 +11,13 @@ This matrix shows which packages dispatch each harness-owned event and which pac
| `agent-loop/config-start-failed` | `emit` | [`packages/core/agent-loop/src/index.ts:199`](../packages/core/agent-loop/src/index.ts) | [`agent-loop`](../packages/core/agent-loop) (`events.dispatch`) | - |
| `agent-preset/selected` | `emit` | [`packages/preset/agent-preset-registry/src/types.ts:66`](../packages/preset/agent-preset-registry/src/types.ts) | [`agent-preset-registry`](../packages/preset/agent-preset-registry) (`emit`) | [`cua`](../packages/experimental/cua), `remotes` |
| `agent/assistant-stream` | `emit` | [`packages/core/agent/src/runtime-types.ts:399`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`headless`](../packages/bundle/headless), `session-controller` |
| `agent/created` | `serial` | [`packages/core/agent/src/runtime-types.ts:290`](../packages/core/agent/src/runtime-types.ts) | [`agent`](../packages/core/agent) (`serial`) | `agent-team`, `browser-use-runtime`, `claude-code-mods`, [`cua`](../packages/experimental/cua), [`file-reference-local`](../packages/context/file-reference-local), [`goal`](../packages/goal/goal), [`goal-round-driver`](../packages/goal/goal-round-driver), [`hooks-claude-code`](../packages/experimental/hooks-claude-code), [`hooks-codex`](../packages/experimental/hooks-codex), [`loader-smoke`](../packages/test-support/loader-smoke), [`permission-presets`](../packages/interaction/permission-presets), `session-controller`, `tool-agent-team`, [`tool-subagent`](../packages/subagent/tool-subagent) |
| `agent/disposed` | `emit` | [`packages/core/agent/src/runtime-types.ts:299`](../packages/core/agent/src/runtime-types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`agent-loop`](../packages/core/agent-loop), `claude-code-mods`, [`file-reference-local`](../packages/context/file-reference-local), [`goal-round-driver`](../packages/goal/goal-round-driver), `session-controller`, [`subagent`](../packages/subagent/subagent), `tool-agent-team`, [`tool-subagent`](../packages/subagent/tool-subagent) |
| `agent/created` | `serial` | [`packages/core/agent/src/runtime-types.ts:290`](../packages/core/agent/src/runtime-types.ts) | [`agent`](../packages/core/agent) (`serial`) | `agent-team`, `browser-use-runtime`, `claude-code-mods`, [`creation-recommendations`](../packages/experimental/creation-recommendations), [`cua`](../packages/experimental/cua), [`file-reference-local`](../packages/context/file-reference-local), [`goal`](../packages/goal/goal), [`goal-round-driver`](../packages/goal/goal-round-driver), [`hooks-claude-code`](../packages/experimental/hooks-claude-code), [`hooks-codex`](../packages/experimental/hooks-codex), [`loader-smoke`](../packages/test-support/loader-smoke), [`permission-presets`](../packages/interaction/permission-presets), `session-controller`, `tool-agent-team`, [`tool-subagent`](../packages/subagent/tool-subagent) |
| `agent/disposed` | `emit` | [`packages/core/agent/src/runtime-types.ts:299`](../packages/core/agent/src/runtime-types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`action-cards`](../packages/experimental/action-cards), [`agent-loop`](../packages/core/agent-loop), `claude-code-mods`, [`creation-recommendations`](../packages/experimental/creation-recommendations), [`file-reference-local`](../packages/context/file-reference-local), [`goal-round-driver`](../packages/goal/goal-round-driver), `session-controller`, [`subagent`](../packages/subagent/subagent), `tool-agent-team`, [`tool-subagent`](../packages/subagent/tool-subagent) |
| `agent/error` | `emit` | [`packages/core/agent/src/runtime-types.ts:429`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`acp`](../packages/acp/acp), [`deepseek-account`](../packages/credentials/deepseek-account), [`goal-round-driver`](../packages/goal/goal-round-driver), [`headless`](../packages/bundle/headless), `server`, `session-controller`, [`session-telemetry`](../packages/session/session-telemetry), [`subagent`](../packages/subagent/subagent) |
| `agent/inbox/claimed` | `emit` | [`packages/core/agent/src/runtime-types.ts:328`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`acp`](../packages/acp/acp), [`goal-round-driver`](../packages/goal/goal-round-driver), [`subagent`](../packages/subagent/subagent), [`tool-jobs`](../packages/jobs/tool-jobs), [`user-questions`](../packages/interaction/user-questions) |
| `agent/inbox/discarded` | `emit` | [`packages/core/agent/src/runtime-types.ts:336`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`goal-round-driver`](../packages/goal/goal-round-driver), [`subagent`](../packages/subagent/subagent), [`user-questions`](../packages/interaction/user-questions) |
| `agent/inbox/inserted` | `emit` | [`packages/core/agent/src/runtime-types.ts:317`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`goal-round-driver`](../packages/goal/goal-round-driver), [`subagent`](../packages/subagent/subagent), [`tool-jobs`](../packages/jobs/tool-jobs) |
| `agent/pre-step` | `waterfall` | [`packages/core/agent/src/runtime-types.ts:350`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`agent`](../packages/core/agent), [`agent-instructions`](../packages/context/agent-instructions), `claude-code-mods`, [`compaction-basic`](../packages/compaction/compaction-basic), [`goal-round-driver`](../packages/goal/goal-round-driver), [`hooks-claude-code`](../packages/experimental/hooks-claude-code), [`hooks-codex`](../packages/experimental/hooks-codex), [`plan-mode`](../packages/plan/plan-mode), [`repeat-tool-reminder`](../packages/guard/repeat-tool-reminder), [`session-checkpoint-policy`](../packages/session/session-checkpoint-policy), `session-controller`, [`session-reference`](../packages/context/session-reference), [`time-context`](../packages/context/time-context), [`tmux-context`](../packages/context/tmux-context), [`tool-skill`](../packages/skill/tool-skill) |
| `agent/pre-step` | `waterfall` | [`packages/core/agent/src/runtime-types.ts:350`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`action-cards`](../packages/experimental/action-cards), [`agent`](../packages/core/agent), [`agent-instructions`](../packages/context/agent-instructions), `claude-code-mods`, [`compaction-basic`](../packages/compaction/compaction-basic), [`goal-round-driver`](../packages/goal/goal-round-driver), [`hooks-claude-code`](../packages/experimental/hooks-claude-code), [`hooks-codex`](../packages/experimental/hooks-codex), [`plan-mode`](../packages/plan/plan-mode), [`repeat-tool-reminder`](../packages/guard/repeat-tool-reminder), [`session-checkpoint-policy`](../packages/session/session-checkpoint-policy), `session-controller`, [`session-reference`](../packages/context/session-reference), [`time-context`](../packages/context/time-context), [`tmux-context`](../packages/context/tmux-context), [`tool-skill`](../packages/skill/tool-skill) |
| `agent/request-error` | `waterfall` | [`packages/core/agent/src/runtime-types.ts:389`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`compaction-basic`](../packages/compaction/compaction-basic), [`compaction-image-offload`](../packages/compaction/compaction-image-offload), [`llm-retry`](../packages/llm/llm-retry) |
| `agent/status` | `emit` | [`packages/core/agent/src/runtime-types.ts:309`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`acp`](../packages/acp/acp), `agent-team`, [`compaction-basic`](../packages/compaction/compaction-basic), [`goal-round-driver`](../packages/goal/goal-round-driver), `server`, `session-controller` |
| `agent/turn-stopping` | `serial` | [`packages/core/agent/src/runtime-types.ts:417`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`serial`) | [`hooks-claude-code`](../packages/experimental/hooks-claude-code), [`hooks-codex`](../packages/experimental/hooks-codex), [`workspace-changes`](../packages/deliverables/workspace-changes) |
@@ -80,7 +80,7 @@ This matrix shows which packages dispatch each harness-owned event and which pac
| `tools/post-execute` | `waterfall` | [`packages/core/tools/src/events.ts:41`](../packages/core/tools/src/events.ts) | [`tools`](../packages/core/tools) (`waterfall`) | `claude-code-mods`, [`hooks-claude-code`](../packages/experimental/hooks-claude-code), [`hooks-codex`](../packages/experimental/hooks-codex), [`repeat-tool-reminder`](../packages/guard/repeat-tool-reminder), [`spill-policy`](../packages/spill/spill-policy), [`tool-fs-search`](../packages/fs/tool-fs-search) |
| `tools/pre-execute` | `waterfall` | [`packages/core/tools/src/events.ts:18`](../packages/core/tools/src/events.ts) | [`tools`](../packages/core/tools) (`waterfall`) | `auto-review`, [`hooks-claude-code`](../packages/experimental/hooks-claude-code), [`hooks-codex`](../packages/experimental/hooks-codex), [`tool-jobs`](../packages/jobs/tool-jobs), [`workspace-changes`](../packages/deliverables/workspace-changes) |
| `tools/ptc-dispatch-log` | `waterfall` | [`packages/core/tool-executor-ptc/src/runtime-types.ts:46`](../packages/core/tool-executor-ptc/src/runtime-types.ts) | [`tool-executor-ptc`](../packages/core/tool-executor-ptc) (`waterfall`) | [`spill-policy`](../packages/spill/spill-policy) |
| `tools/result` | `emit` | [`packages/core/tools/src/events.ts:49`](../packages/core/tools/src/events.ts) | [`tools`](../packages/core/tools) (`events.dispatch`) | [`agent-instructions`](../packages/context/agent-instructions), `claude-code-mods`, [`subagent`](../packages/subagent/subagent), [`tool-present`](../packages/deliverables/tool-present) |
| `tools/result` | `emit` | [`packages/core/tools/src/events.ts:49`](../packages/core/tools/src/events.ts) | [`tools`](../packages/core/tools) (`events.dispatch`) | [`action-cards`](../packages/experimental/action-cards), [`agent-instructions`](../packages/context/agent-instructions), `claude-code-mods`, [`subagent`](../packages/subagent/subagent), [`tool-present`](../packages/deliverables/tool-present) |
| `user-questions/request` | `waterfall` | [`packages/interaction/user-questions/src/types.ts:174`](../packages/interaction/user-questions/src/types.ts) | [`user-questions`](../packages/interaction/user-questions) (`waterfall`) | `remotes`, `server` |
| `webserver/index-inject` | `emit` | [`packages/host/webserver/src/index.ts:44`](../packages/host/webserver/src/index.ts) | `webserver` (`emit`) | `connection`, `inspector`, `modules`, `remote-access`, `shortcuts`, `ui-sidebar-documentpreview`, `ui-theme` |
| `workflow/agent-end` | `emit` | [`packages/workflow/workflow/src/index.ts:79`](../packages/workflow/workflow/src/index.ts) | [`workflow`](../packages/workflow/workflow) (`events.dispatch`) | [`tool-workflow`](../packages/workflow/tool-workflow) |
+4 -4
View File
@@ -13,13 +13,13 @@
| `agent-loop/config-start-failed` | `emit` | [`packages/core/agent-loop/src/index.ts:199`](../packages/core/agent-loop/src/index.ts) | [`agent-loop`](../packages/core/agent-loop) (`events.dispatch`) | - |
| `agent-preset/selected` | `emit` | [`packages/preset/agent-preset-registry/src/types.ts:66`](../packages/preset/agent-preset-registry/src/types.ts) | [`agent-preset-registry`](../packages/preset/agent-preset-registry) (`emit`) | [`cua`](../packages/experimental/cua), `remotes` |
| `agent/assistant-stream` | `emit` | [`packages/core/agent/src/runtime-types.ts:399`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`headless`](../packages/bundle/headless), `session-controller` |
| `agent/created` | `serial` | [`packages/core/agent/src/runtime-types.ts:290`](../packages/core/agent/src/runtime-types.ts) | [`agent`](../packages/core/agent) (`serial`) | `agent-team`, `browser-use-runtime`, `claude-code-mods`, [`cua`](../packages/experimental/cua), [`file-reference-local`](../packages/context/file-reference-local), [`goal`](../packages/goal/goal), [`goal-round-driver`](../packages/goal/goal-round-driver), [`hooks-claude-code`](../packages/experimental/hooks-claude-code), [`hooks-codex`](../packages/experimental/hooks-codex), [`loader-smoke`](../packages/test-support/loader-smoke), [`permission-presets`](../packages/interaction/permission-presets), `session-controller`, `tool-agent-team`, [`tool-subagent`](../packages/subagent/tool-subagent) |
| `agent/disposed` | `emit` | [`packages/core/agent/src/runtime-types.ts:299`](../packages/core/agent/src/runtime-types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`agent-loop`](../packages/core/agent-loop), `claude-code-mods`, [`file-reference-local`](../packages/context/file-reference-local), [`goal-round-driver`](../packages/goal/goal-round-driver), `session-controller`, [`subagent`](../packages/subagent/subagent), `tool-agent-team`, [`tool-subagent`](../packages/subagent/tool-subagent) |
| `agent/created` | `serial` | [`packages/core/agent/src/runtime-types.ts:290`](../packages/core/agent/src/runtime-types.ts) | [`agent`](../packages/core/agent) (`serial`) | `agent-team`, `browser-use-runtime`, `claude-code-mods`, [`creation-recommendations`](../packages/experimental/creation-recommendations), [`cua`](../packages/experimental/cua), [`file-reference-local`](../packages/context/file-reference-local), [`goal`](../packages/goal/goal), [`goal-round-driver`](../packages/goal/goal-round-driver), [`hooks-claude-code`](../packages/experimental/hooks-claude-code), [`hooks-codex`](../packages/experimental/hooks-codex), [`loader-smoke`](../packages/test-support/loader-smoke), [`permission-presets`](../packages/interaction/permission-presets), `session-controller`, `tool-agent-team`, [`tool-subagent`](../packages/subagent/tool-subagent) |
| `agent/disposed` | `emit` | [`packages/core/agent/src/runtime-types.ts:299`](../packages/core/agent/src/runtime-types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`action-cards`](../packages/experimental/action-cards), [`agent-loop`](../packages/core/agent-loop), `claude-code-mods`, [`creation-recommendations`](../packages/experimental/creation-recommendations), [`file-reference-local`](../packages/context/file-reference-local), [`goal-round-driver`](../packages/goal/goal-round-driver), `session-controller`, [`subagent`](../packages/subagent/subagent), `tool-agent-team`, [`tool-subagent`](../packages/subagent/tool-subagent) |
| `agent/error` | `emit` | [`packages/core/agent/src/runtime-types.ts:429`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`acp`](../packages/acp/acp), [`deepseek-account`](../packages/credentials/deepseek-account), [`goal-round-driver`](../packages/goal/goal-round-driver), [`headless`](../packages/bundle/headless), `server`, `session-controller`, [`session-telemetry`](../packages/session/session-telemetry), [`subagent`](../packages/subagent/subagent) |
| `agent/inbox/claimed` | `emit` | [`packages/core/agent/src/runtime-types.ts:328`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`acp`](../packages/acp/acp), [`goal-round-driver`](../packages/goal/goal-round-driver), [`subagent`](../packages/subagent/subagent), [`tool-jobs`](../packages/jobs/tool-jobs), [`user-questions`](../packages/interaction/user-questions) |
| `agent/inbox/discarded` | `emit` | [`packages/core/agent/src/runtime-types.ts:336`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`goal-round-driver`](../packages/goal/goal-round-driver), [`subagent`](../packages/subagent/subagent), [`user-questions`](../packages/interaction/user-questions) |
| `agent/inbox/inserted` | `emit` | [`packages/core/agent/src/runtime-types.ts:317`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`goal-round-driver`](../packages/goal/goal-round-driver), [`subagent`](../packages/subagent/subagent), [`tool-jobs`](../packages/jobs/tool-jobs) |
| `agent/pre-step` | `waterfall` | [`packages/core/agent/src/runtime-types.ts:350`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`agent`](../packages/core/agent), [`agent-instructions`](../packages/context/agent-instructions), `claude-code-mods`, [`compaction-basic`](../packages/compaction/compaction-basic), [`goal-round-driver`](../packages/goal/goal-round-driver), [`hooks-claude-code`](../packages/experimental/hooks-claude-code), [`hooks-codex`](../packages/experimental/hooks-codex), [`plan-mode`](../packages/plan/plan-mode), [`repeat-tool-reminder`](../packages/guard/repeat-tool-reminder), [`session-checkpoint-policy`](../packages/session/session-checkpoint-policy), `session-controller`, [`session-reference`](../packages/context/session-reference), [`time-context`](../packages/context/time-context), [`tmux-context`](../packages/context/tmux-context), [`tool-skill`](../packages/skill/tool-skill) |
| `agent/pre-step` | `waterfall` | [`packages/core/agent/src/runtime-types.ts:350`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`action-cards`](../packages/experimental/action-cards), [`agent`](../packages/core/agent), [`agent-instructions`](../packages/context/agent-instructions), `claude-code-mods`, [`compaction-basic`](../packages/compaction/compaction-basic), [`goal-round-driver`](../packages/goal/goal-round-driver), [`hooks-claude-code`](../packages/experimental/hooks-claude-code), [`hooks-codex`](../packages/experimental/hooks-codex), [`plan-mode`](../packages/plan/plan-mode), [`repeat-tool-reminder`](../packages/guard/repeat-tool-reminder), [`session-checkpoint-policy`](../packages/session/session-checkpoint-policy), `session-controller`, [`session-reference`](../packages/context/session-reference), [`time-context`](../packages/context/time-context), [`tmux-context`](../packages/context/tmux-context), [`tool-skill`](../packages/skill/tool-skill) |
| `agent/request-error` | `waterfall` | [`packages/core/agent/src/runtime-types.ts:389`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`compaction-basic`](../packages/compaction/compaction-basic), [`compaction-image-offload`](../packages/compaction/compaction-image-offload), [`llm-retry`](../packages/llm/llm-retry) |
| `agent/status` | `emit` | [`packages/core/agent/src/runtime-types.ts:309`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`acp`](../packages/acp/acp), `agent-team`, [`compaction-basic`](../packages/compaction/compaction-basic), [`goal-round-driver`](../packages/goal/goal-round-driver), `server`, `session-controller` |
| `agent/turn-stopping` | `serial` | [`packages/core/agent/src/runtime-types.ts:417`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`serial`) | [`hooks-claude-code`](../packages/experimental/hooks-claude-code), [`hooks-codex`](../packages/experimental/hooks-codex), [`workspace-changes`](../packages/deliverables/workspace-changes) |
@@ -82,7 +82,7 @@
| `tools/post-execute` | `waterfall` | [`packages/core/tools/src/events.ts:41`](../packages/core/tools/src/events.ts) | [`tools`](../packages/core/tools) (`waterfall`) | `claude-code-mods`, [`hooks-claude-code`](../packages/experimental/hooks-claude-code), [`hooks-codex`](../packages/experimental/hooks-codex), [`repeat-tool-reminder`](../packages/guard/repeat-tool-reminder), [`spill-policy`](../packages/spill/spill-policy), [`tool-fs-search`](../packages/fs/tool-fs-search) |
| `tools/pre-execute` | `waterfall` | [`packages/core/tools/src/events.ts:18`](../packages/core/tools/src/events.ts) | [`tools`](../packages/core/tools) (`waterfall`) | `auto-review`, [`hooks-claude-code`](../packages/experimental/hooks-claude-code), [`hooks-codex`](../packages/experimental/hooks-codex), [`tool-jobs`](../packages/jobs/tool-jobs), [`workspace-changes`](../packages/deliverables/workspace-changes) |
| `tools/ptc-dispatch-log` | `waterfall` | [`packages/core/tool-executor-ptc/src/runtime-types.ts:46`](../packages/core/tool-executor-ptc/src/runtime-types.ts) | [`tool-executor-ptc`](../packages/core/tool-executor-ptc) (`waterfall`) | [`spill-policy`](../packages/spill/spill-policy) |
| `tools/result` | `emit` | [`packages/core/tools/src/events.ts:49`](../packages/core/tools/src/events.ts) | [`tools`](../packages/core/tools) (`events.dispatch`) | [`agent-instructions`](../packages/context/agent-instructions), `claude-code-mods`, [`subagent`](../packages/subagent/subagent), [`tool-present`](../packages/deliverables/tool-present) |
| `tools/result` | `emit` | [`packages/core/tools/src/events.ts:49`](../packages/core/tools/src/events.ts) | [`tools`](../packages/core/tools) (`events.dispatch`) | [`action-cards`](../packages/experimental/action-cards), [`agent-instructions`](../packages/context/agent-instructions), `claude-code-mods`, [`subagent`](../packages/subagent/subagent), [`tool-present`](../packages/deliverables/tool-present) |
| `user-questions/request` | `waterfall` | [`packages/interaction/user-questions/src/types.ts:174`](../packages/interaction/user-questions/src/types.ts) | [`user-questions`](../packages/interaction/user-questions) (`waterfall`) | `remotes`, `server` |
| `webserver/index-inject` | `emit` | [`packages/host/webserver/src/index.ts:44`](../packages/host/webserver/src/index.ts) | `webserver` (`emit`) | `connection`, `inspector`, `modules`, `remote-access`, `shortcuts`, `ui-sidebar-documentpreview`, `ui-theme` |
| `workflow/agent-end` | `emit` | [`packages/workflow/workflow/src/index.ts:79`](../packages/workflow/workflow/src/index.ts) | [`workflow`](../packages/workflow/workflow) (`events.dispatch`) | [`tool-workflow`](../packages/workflow/tool-workflow) |
@@ -6,5 +6,5 @@
en: a11f106978c9dce7
zh: a11f106978c9dce7
/experimental-plugin-record-catalog:
en: 84dbc9c06ab92877
zh: b82ef2a46292ab0e
en: cb931db3b4bcbe33
zh: aca9cfecfdee34c0
+1
View File
@@ -13,5 +13,6 @@ Shared Session event declarations and resolved schemas appear in the [Session pe
| Record | Owner | Purpose | Payload and source |
|---|---|---|---|
| `plugin:action-cards/decision` | `@deepseek-ai/dsh-action-cards` | Dismissals and kind-defined progress of action cards. | <code>ActionCardDecision</code> · [`packages/experimental/action-cards/src/types.ts`](../packages/experimental/action-cards/src/types.ts) |
| `plugin:translator/request` | `@deepseek-ai/dsh-experimental-translator` | Provider-independent translation input, retained before dispatch. | <code>TranslationRequestRecord</code> · [`packages/experimental/translator/src/types.ts`](../packages/experimental/translator/src/types.ts) |
| `plugin:translator/result` | `@deepseek-ai/dsh-experimental-translator` | Successful translation, retained before it is returned to the consumer. | <code>TranslationResultRecord</code> · [`packages/experimental/translator/src/types.ts`](../packages/experimental/translator/src/types.ts) |
@@ -13,5 +13,6 @@
| 记录 | 所属包 | 用途 | 载荷与源码 |
|---|---|---|---|
| `plugin:action-cards/decision` | `@deepseek-ai/dsh-action-cards` | Dismissals and kind-defined progress of action cards. | <code>ActionCardDecision</code> · [`packages/experimental/action-cards/src/types.ts`](../packages/experimental/action-cards/src/types.ts) |
| `plugin:translator/request` | `@deepseek-ai/dsh-experimental-translator` | Provider-independent translation input, retained before dispatch. | <code>TranslationRequestRecord</code> · [`packages/experimental/translator/src/types.ts`](../packages/experimental/translator/src/types.ts) |
| `plugin:translator/result` | `@deepseek-ai/dsh-experimental-translator` | Successful translation, retained before it is returned to the consumer. | <code>TranslationResultRecord</code> · [`packages/experimental/translator/src/types.ts`](../packages/experimental/translator/src/types.ts) |
+23
View File
@@ -254,6 +254,11 @@ flowchart TD
pkg_office_to_pdf["office-to-pdf"]
end
subgraph group_experimental["packages/experimental"]
pkg_action_cards["action-cards"]
pkg_client_ui_action_cards["client-ui-action-cards"]
pkg_client_ui_creation_recommendations["client-ui-creation-recommendations"]
pkg_client_ui_plugin_recommendations["client-ui-plugin-recommendations"]
pkg_creation_recommendations["creation-recommendations"]
pkg_cua["cua"]
pkg_cua_bundle["cua-bundle"]
pkg_experimental_agent_team["experimental-agent-team"]
@@ -297,6 +302,7 @@ flowchart TD
pkg_hook_protocol["hook-protocol"]
pkg_hooks_claude_code["hooks-claude-code"]
pkg_hooks_codex["hooks-codex"]
pkg_plugin_recommendations["plugin-recommendations"]
pkg_session_title_all_prompts_llm["session-title-all-prompts-llm"]
pkg_skill_badge["skill-badge"]
pkg_tool_ralph["tool-ralph"]
@@ -515,6 +521,9 @@ flowchart TD
pkg_experimental_webworker_runtime --> pkg_client_connection
pkg_experimental_webworker_runtime --> pkg_client_modules
pkg_experimental_webworker_runtime --> pkg_host_webserver
pkg_plugin_recommendations --> pkg_llm
pkg_plugin_recommendations --> pkg_skill
pkg_plugin_recommendations --> pkg_typert_protocol
pkg_skill_badge --> pkg_skill
pkg_host_directory_picker_auto --> pkg_client_connection
pkg_host_directory_picker_auto --> pkg_client_ui_directory_picker_browse
@@ -924,6 +933,14 @@ flowchart TD
pkg_workspace_changes --> pkg_session
pkg_workspace_changes --> pkg_subprocess
pkg_workspace_changes --> pkg_tools
pkg_action_cards --> pkg_llm
pkg_action_cards --> pkg_session
pkg_action_cards --> pkg_tools
pkg_action_cards --> pkg_typert_protocol
pkg_creation_recommendations --> pkg_llm
pkg_creation_recommendations --> pkg_session
pkg_creation_recommendations --> pkg_skill
pkg_creation_recommendations --> pkg_tools
pkg_experimental_browser_use_chrome_devtools_mcp --> pkg_agent
pkg_experimental_browser_use_chrome_devtools_mcp --> pkg_browser_use
pkg_experimental_browser_use_chrome_devtools_mcp --> pkg_system_prompt
@@ -1560,6 +1577,9 @@ flowchart TD
| [`client-web`](../packages/client/web) | `client` | — |
| [`credentials`](../packages/credentials/credentials) | `credentials` | — |
| [`office-to-pdf`](../packages/document/office-to-pdf) | `document` | — |
| [`client-ui-action-cards`](../packages/experimental/client-ui-action-cards) | `experimental` | — |
| [`client-ui-creation-recommendations`](../packages/experimental/client-ui-creation-recommendations) | `experimental` | — |
| [`client-ui-plugin-recommendations`](../packages/experimental/client-ui-plugin-recommendations) | `experimental` | — |
| [`cua-bundle`](../packages/experimental/cua-bundle) | `experimental` | — |
| [`experimental-agent-team-profile`](../packages/experimental/agent-team-profile) | `experimental` | — |
| [`experimental-badge-skill-bundle`](../packages/experimental/badge-skill-bundle) | `experimental` | — |
@@ -1628,6 +1648,7 @@ flowchart TD
| [`deliverable-file-references`](../packages/deliverables/deliverable-file-references) | `deliverables` | [`system-prompt`](../packages/core/system-prompt) |
| [`experimental-computer-use-cua-driver-mcp`](../packages/experimental/computer-use-cua-driver-mcp) | `experimental` | [`computer-use`](../packages/computer-use/computer-use) |
| [`experimental-webworker-runtime`](../packages/experimental/webworker-runtime) | `experimental` | [`client-connection`](../packages/client/connection), [`client-modules`](../packages/client/modules), [`host-webserver`](../packages/host/webserver) |
| [`plugin-recommendations`](../packages/experimental/plugin-recommendations) | `experimental` | [`llm`](../packages/llm/llm), [`skill`](../packages/skill/skill), [`typert-protocol`](../packages/typert/protocol) |
| [`skill-badge`](../packages/experimental/skill-badge) | `experimental` | [`skill`](../packages/skill/skill) |
| [`host-directory-picker-auto`](../packages/host/directory-picker-auto) | `host` | [`client-connection`](../packages/client/connection), [`client-ui-directory-picker-browse`](../packages/client/ui-directory-picker-browse), [`client-ui-directory-picker-native`](../packages/client/ui-directory-picker-native), [`host-directory-picker-browse`](../packages/host/directory-picker-browse), [`host-directory-picker-native`](../packages/host/directory-picker-native), [`host-webserver`](../packages/host/webserver) |
| [`host-frontend-static`](../packages/host/frontend-static) | `host` | [`client-connection`](../packages/client/connection), [`host-webserver`](../packages/host/webserver) |
@@ -1730,6 +1751,8 @@ flowchart TD
| [`file-reference-local`](../packages/context/file-reference-local) | `context` | [`agent`](../packages/core/agent), [`file-reference`](../packages/context/file-reference), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`working-directory`](../packages/session/working-directory) |
| [`tool-present`](../packages/deliverables/tool-present) | `deliverables` | [`agent`](../packages/core/agent), [`fs`](../packages/fs/fs), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`tools`](../packages/core/tools), [`working-directory`](../packages/session/working-directory) |
| [`workspace-changes`](../packages/deliverables/workspace-changes) | `deliverables` | [`agent`](../packages/core/agent), [`fs`](../packages/fs/fs), [`session`](../packages/core/session), [`subprocess`](../packages/subprocess/subprocess), [`tools`](../packages/core/tools) |
| [`action-cards`](../packages/experimental/action-cards) | `experimental` | [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`tools`](../packages/core/tools), [`typert-protocol`](../packages/typert/protocol) |
| [`creation-recommendations`](../packages/experimental/creation-recommendations) | `experimental` | [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`skill`](../packages/skill/skill), [`tools`](../packages/core/tools) |
| [`experimental-browser-use-chrome-devtools-mcp`](../packages/experimental/browser-use-chrome-devtools-mcp) | `experimental` | [`agent`](../packages/core/agent), [`browser-use`](../packages/browser-use/browser-use), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`working-directory`](../packages/session/working-directory) |
| [`experimental-browser-use-playwright-mcp`](../packages/experimental/browser-use-playwright-mcp) | `experimental` | [`agent`](../packages/core/agent), [`browser-use`](../packages/browser-use/browser-use), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`working-directory`](../packages/session/working-directory) |
| [`experimental-browser-use-stagehand-native`](../packages/experimental/browser-use-stagehand-native) | `experimental` | [`agent`](../packages/core/agent), [`browser-use`](../packages/browser-use/browser-use), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
+23
View File
@@ -256,6 +256,11 @@ flowchart TD
pkg_office_to_pdf["office-to-pdf"]
end
subgraph group_experimental["packages/experimental"]
pkg_action_cards["action-cards"]
pkg_client_ui_action_cards["client-ui-action-cards"]
pkg_client_ui_creation_recommendations["client-ui-creation-recommendations"]
pkg_client_ui_plugin_recommendations["client-ui-plugin-recommendations"]
pkg_creation_recommendations["creation-recommendations"]
pkg_cua["cua"]
pkg_cua_bundle["cua-bundle"]
pkg_experimental_agent_team["experimental-agent-team"]
@@ -299,6 +304,7 @@ flowchart TD
pkg_hook_protocol["hook-protocol"]
pkg_hooks_claude_code["hooks-claude-code"]
pkg_hooks_codex["hooks-codex"]
pkg_plugin_recommendations["plugin-recommendations"]
pkg_session_title_all_prompts_llm["session-title-all-prompts-llm"]
pkg_skill_badge["skill-badge"]
pkg_tool_ralph["tool-ralph"]
@@ -517,6 +523,9 @@ flowchart TD
pkg_experimental_webworker_runtime --> pkg_client_connection
pkg_experimental_webworker_runtime --> pkg_client_modules
pkg_experimental_webworker_runtime --> pkg_host_webserver
pkg_plugin_recommendations --> pkg_llm
pkg_plugin_recommendations --> pkg_skill
pkg_plugin_recommendations --> pkg_typert_protocol
pkg_skill_badge --> pkg_skill
pkg_host_directory_picker_auto --> pkg_client_connection
pkg_host_directory_picker_auto --> pkg_client_ui_directory_picker_browse
@@ -926,6 +935,14 @@ flowchart TD
pkg_workspace_changes --> pkg_session
pkg_workspace_changes --> pkg_subprocess
pkg_workspace_changes --> pkg_tools
pkg_action_cards --> pkg_llm
pkg_action_cards --> pkg_session
pkg_action_cards --> pkg_tools
pkg_action_cards --> pkg_typert_protocol
pkg_creation_recommendations --> pkg_llm
pkg_creation_recommendations --> pkg_session
pkg_creation_recommendations --> pkg_skill
pkg_creation_recommendations --> pkg_tools
pkg_experimental_browser_use_chrome_devtools_mcp --> pkg_agent
pkg_experimental_browser_use_chrome_devtools_mcp --> pkg_browser_use
pkg_experimental_browser_use_chrome_devtools_mcp --> pkg_system_prompt
@@ -1562,6 +1579,9 @@ flowchart TD
| [`client-web`](../packages/client/web) | `client` | — |
| [`credentials`](../packages/credentials/credentials) | `credentials` | — |
| [`office-to-pdf`](../packages/document/office-to-pdf) | `document` | — |
| [`client-ui-action-cards`](../packages/experimental/client-ui-action-cards) | `experimental` | — |
| [`client-ui-creation-recommendations`](../packages/experimental/client-ui-creation-recommendations) | `experimental` | — |
| [`client-ui-plugin-recommendations`](../packages/experimental/client-ui-plugin-recommendations) | `experimental` | — |
| [`cua-bundle`](../packages/experimental/cua-bundle) | `experimental` | — |
| [`experimental-agent-team-profile`](../packages/experimental/agent-team-profile) | `experimental` | — |
| [`experimental-badge-skill-bundle`](../packages/experimental/badge-skill-bundle) | `experimental` | — |
@@ -1630,6 +1650,7 @@ flowchart TD
| [`deliverable-file-references`](../packages/deliverables/deliverable-file-references) | `deliverables` | [`system-prompt`](../packages/core/system-prompt) |
| [`experimental-computer-use-cua-driver-mcp`](../packages/experimental/computer-use-cua-driver-mcp) | `experimental` | [`computer-use`](../packages/computer-use/computer-use) |
| [`experimental-webworker-runtime`](../packages/experimental/webworker-runtime) | `experimental` | [`client-connection`](../packages/client/connection), [`client-modules`](../packages/client/modules), [`host-webserver`](../packages/host/webserver) |
| [`plugin-recommendations`](../packages/experimental/plugin-recommendations) | `experimental` | [`llm`](../packages/llm/llm), [`skill`](../packages/skill/skill), [`typert-protocol`](../packages/typert/protocol) |
| [`skill-badge`](../packages/experimental/skill-badge) | `experimental` | [`skill`](../packages/skill/skill) |
| [`host-directory-picker-auto`](../packages/host/directory-picker-auto) | `host` | [`client-connection`](../packages/client/connection), [`client-ui-directory-picker-browse`](../packages/client/ui-directory-picker-browse), [`client-ui-directory-picker-native`](../packages/client/ui-directory-picker-native), [`host-directory-picker-browse`](../packages/host/directory-picker-browse), [`host-directory-picker-native`](../packages/host/directory-picker-native), [`host-webserver`](../packages/host/webserver) |
| [`host-frontend-static`](../packages/host/frontend-static) | `host` | [`client-connection`](../packages/client/connection), [`host-webserver`](../packages/host/webserver) |
@@ -1732,6 +1753,8 @@ flowchart TD
| [`file-reference-local`](../packages/context/file-reference-local) | `context` | [`agent`](../packages/core/agent), [`file-reference`](../packages/context/file-reference), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`working-directory`](../packages/session/working-directory) |
| [`tool-present`](../packages/deliverables/tool-present) | `deliverables` | [`agent`](../packages/core/agent), [`fs`](../packages/fs/fs), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`tools`](../packages/core/tools), [`working-directory`](../packages/session/working-directory) |
| [`workspace-changes`](../packages/deliverables/workspace-changes) | `deliverables` | [`agent`](../packages/core/agent), [`fs`](../packages/fs/fs), [`session`](../packages/core/session), [`subprocess`](../packages/subprocess/subprocess), [`tools`](../packages/core/tools) |
| [`action-cards`](../packages/experimental/action-cards) | `experimental` | [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`tools`](../packages/core/tools), [`typert-protocol`](../packages/typert/protocol) |
| [`creation-recommendations`](../packages/experimental/creation-recommendations) | `experimental` | [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`skill`](../packages/skill/skill), [`tools`](../packages/core/tools) |
| [`experimental-browser-use-chrome-devtools-mcp`](../packages/experimental/browser-use-chrome-devtools-mcp) | `experimental` | [`agent`](../packages/core/agent), [`browser-use`](../packages/browser-use/browser-use), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`working-directory`](../packages/session/working-directory) |
| [`experimental-browser-use-playwright-mcp`](../packages/experimental/browser-use-playwright-mcp) | `experimental` | [`agent`](../packages/core/agent), [`browser-use`](../packages/browser-use/browser-use), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`working-directory`](../packages/session/working-directory) |
| [`experimental-browser-use-stagehand-native`](../packages/experimental/browser-use-stagehand-native) | `experimental` | [`agent`](../packages/core/agent), [`browser-use`](../packages/browser-use/browser-use), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
+78 -54
View File
@@ -9,8 +9,8 @@
en: 1387c4505dde2faf
zh: 48570fa1c13d6910
/session-persistence-event-catalog/persistence-type-fingerprints:
en: bcff48893d904af8
zh: f82e75c91c39adb1
en: 3e24343c4745793d
zh: 90f808fd6b79c5a3
/session-persistence-event-catalog/event-envelope:
en: 981db6b1928eda50
zh: d77bc66cf678099d
@@ -309,8 +309,11 @@
en: 3493ecf0993e5397
zh: ec95688f593a4ed4
/session-persistence-event-catalog/resolved-persistence-types/aborted:
en: d716fb9ee9baa564
zh: d716fb9ee9baa564
en: 40049fd36471e4b2
zh: 40049fd36471e4b2
/session-persistence-event-catalog/resolved-persistence-types/action-cards:
en: 629f821b50a38bc6
zh: 629f821b50a38bc6
/session-persistence-event-catalog/resolved-persistence-types/active:
en: 6b343e390ae7d7a7
zh: 6b343e390ae7d7a7
@@ -1005,11 +1008,11 @@
en: a6a2e38fdd6d127a
zh: 1fe82356d89f5769
/session-persistence-event-catalog/resolved-persistence-types/deepseeksearchllmrequest:
en: 128c0ecc1897eb68
zh: e4c67c92752bcc2a
en: 0e286bae74e78505
zh: b1c9da5aa56d5a81
/session-persistence-event-catalog/resolved-persistence-types/developermessage:
en: 997b5db27df00446
zh: 07e5197ddd7bd59b
en: 3d8a393be23efcde
zh: f95f337d816dbbe7
/session-persistence-event-catalog/resolved-persistence-types/epochheader:
en: c804cb9e2e31c5ec
zh: 3ed7ac59cd1441b7
@@ -1119,11 +1122,11 @@
en: 6dbe099c62f7c226
zh: 6fd788f63c038c91
/session-persistence-event-catalog/resolved-persistence-types/llmretrystartedeventdata:
en: c2d9edd8d9f492b7
zh: f59c687ac691f7ce
en: 3d2f7c88259b6446
zh: 58bee68c335d6d4f
/session-persistence-event-catalog/resolved-persistence-types/message:
en: 594707e4894819a8
zh: 268c4bfd5c4c28f2
en: 76d13d39fe22f7bd
zh: b1e19cfe90a6d6b6
/session-persistence-event-catalog/resolved-persistence-types/messagefeedbackdelete:
en: c582953270d4ca44
zh: 966ccfe2e7660a2a
@@ -1134,14 +1137,14 @@
en: f58575a1fc96df42
zh: 9f0c6ed9033e5c9d
/session-persistence-event-catalog/resolved-persistence-types/messagefeedbackrating:
en: 674296d54c1914d0
zh: 3d71ab319514be25
en: 887549d6401dd116
zh: 30198672b67dac6b
/session-persistence-event-catalog/resolved-persistence-types/messagesource:
en: 3fab179fe8d58c31
zh: 5328c9161ff908a9
en: 2f21414632f8d184
zh: d85f66e001fd814c
/session-persistence-event-catalog/resolved-persistence-types/message~2:
en: 8bd12543fcdcda5f
zh: d4c6b17083948856
en: 87e95476b4985e5e
zh: 0e0322e005b3e995
/session-persistence-event-catalog/resolved-persistence-types/modelmessagesource:
en: 5f9154ca34235ab9
zh: a833105910ebfa82
@@ -1203,11 +1206,11 @@
en: 9a54c4478ad3b620
zh: 02c4c1fd7e252770
/session-persistence-event-catalog/resolved-persistence-types/sessiontitleeventdata:
en: bf7325029c9338d6
zh: e5f98271511e5318
en: c0104327b481ae54
zh: b4624d112bee37f1
/session-persistence-event-catalog/resolved-persistence-types/sessiontitlellmrequesteventdata:
en: 89dcb9a9b957c126
zh: 132aeb7dfd435290
en: 2e40681d575da503
zh: 57cbccb16548e2da
/session-persistence-event-catalog/resolved-persistence-types/sessiontitlemodelidentity:
en: 9531468264499498
zh: 9da23c0ad2776563
@@ -1314,14 +1317,14 @@
en: 4dc89c072fe5d0d1
zh: 4ed2c9e9e8b4f7e2
/session-persistence-event-catalog/resolved-persistence-types/turnendreason:
en: 344a7207a4199a32
zh: d500af39e83289cf
en: e39f2f99ea109af9
zh: f95ec41c72738f3e
/session-persistence-event-catalog/resolved-persistence-types/usermessage:
en: bd3aedd570ded4d2
zh: f020d0816d3a6c1a
en: 79b3ac6c731859aa
zh: 81058fe7edf61bdb
/session-persistence-event-catalog/resolved-persistence-types/usermessage~2:
en: b99d66ffe277f0bc
zh: eacc9c1f62ae66c1
en: 8e699c0217638bbe
zh: a2047db24c25217b
/session-persistence-event-catalog/resolved-persistence-types/workflowagentoutcome:
en: d0a3f9aab18c3de5
zh: 76ac69a835ca4e29
@@ -1500,11 +1503,11 @@
en: 51af619ed8532a43
zh: a5ad878c63d0e046
/session-persistence-event-catalog/resolved-persistence-types/header-reason-startsseries:
en: 0b1ca5c765d716d1
zh: dd939095c250c4eb
en: 8c523b44a081ffe0
zh: d94ac2402fe80645
/session-persistence-event-catalog/resolved-persistence-types/headerseq-message-step-turn:
en: 3d16abb7b9581429
zh: dc959157864261d8
en: 174b2c8332e397b2
zh: 31b94e9a33b9bdac
/session-persistence-event-catalog/resolved-persistence-types/height-width:
en: a5df50710083ac25
zh: aad3ccf6f6de70e4
@@ -1521,17 +1524,38 @@
en: 41f73c58a77dfa28
zh: af77f4aee3cb4a40
/session-persistence-event-catalog/resolved-persistence-types/inherited:
en: 6ce6ddac6d815805
zh: f7196176e902c965
en: bbffa2689f69314f
zh: c691059ad7b99d67
/session-persistence-event-catalog/resolved-persistence-types/inserted-outcome-removedcount-start:
en: 346d884a7a74e48d
zh: 646ac1021f9ca920
en: 1750502af87d698c
zh: 382a3356f0a61546
/session-persistence-event-catalog/resolved-persistence-types/interrupted-message-step-stream:
en: 89fb3f919d181c86
zh: 4380624236500687
/session-persistence-event-catalog/resolved-persistence-types/kind-aborted-reason:
en: 48424cbed3fbc269
zh: 2ce88c04ad002104
en: 8bdd31bfea14a015
zh: 203d7ebf5ea4d352
/session-persistence-event-catalog/resolved-persistence-types/kind-action-cards:
en: 8d057f0bd0b88fc6
zh: 500d1e3d54e6d7c4
/session-persistence-event-catalog/resolved-persistence-types/kind-action-cards-form-catalog:
en: 14e9dd01c49f91e0
zh: 4a7e4e4aeeb15982
/session-persistence-event-catalog/resolved-persistence-types/kind-action-cards-form-instructions:
en: f59ec0c37dc8f9b6
zh: debc915ed98581fb
/session-persistence-event-catalog/resolved-persistence-types/kind-action-cards-form-notice:
en: ef0a456530698cca
zh: 23f2ace5345eb847
/session-persistence-event-catalog/resolved-persistence-types/kind-action-cards-form-recall:
en: 0b607f9577e3a3c5
zh: 237ed72b7a4154df
/session-persistence-event-catalog/resolved-persistence-types/kind-action-cards-form-relay:
en: 712bc9ad7c51a8fe
zh: f230dc9c09bdbf3c
/session-persistence-event-catalog/resolved-persistence-types/kind-action-cards-form-snapshot:
en: 4e030b06ef900819
zh: 27532bbabe118206
/session-persistence-event-catalog/resolved-persistence-types/kind-blocked:
en: 2fad818b19823c48
zh: a6b33dee21c3fdc9
@@ -1920,11 +1944,11 @@
en: ea32b4165dbfb182
zh: 57f48af06cfa201c
/session-persistence-event-catalog/resolved-persistence-types/type-agent-preset-selected:
en: c8c731ee23bed85a
zh: f4ffc80f7828701d
en: 6a59f1cd472759e9
zh: 1db67f4f97207efc
/session-persistence-event-catalog/resolved-persistence-types/type-agent-inbox-spliced:
en: 62dfe9447efaceb1
zh: 076454de19b5d7d7
en: 6c276128e62a5432
zh: 4ff5e93d255caece
/session-persistence-event-catalog/resolved-persistence-types/type-approval-asked:
en: 9c77f18b0dad22f0
zh: 8894cf2878fe90db
@@ -1968,11 +1992,11 @@
en: ba73fb10b7e2ffc9
zh: 8d5c97e63e5f16da
/session-persistence-event-catalog/resolved-persistence-types/type-deliverables-presented:
en: a4d9eab2928e9b30
zh: b36702c866518273
en: 10d95ce64c285e64
zh: 0ddb26f1fa16629c
/session-persistence-event-catalog/resolved-persistence-types/type-developer-message:
en: a9e6e24b98390923
zh: 65addae898d8aaec
en: 78d421c83506ebe9
zh: 2e9399ba2d360627
/session-persistence-event-catalog/resolved-persistence-types/type-disabled:
en: 585627a50ece4e5f
zh: 157837aee3da0ac3
@@ -2046,11 +2070,11 @@
en: acf7e21e4d21ebff
zh: fb83a853634d204a
/session-persistence-event-catalog/resolved-persistence-types/type-session-title:
en: b99fbacda0db1abd
zh: 9e75ce6b1d174d48
en: 26d99fabe31f7fa3
zh: 0a44b5c8f7f6488c
/session-persistence-event-catalog/resolved-persistence-types/type-session-title-llm-request:
en: 7ba30b98d8e3f653
zh: ca68b01ab4b6ba3d
en: bbbb345651600abf
zh: e3b29e05fecb136a
/session-persistence-event-catalog/resolved-persistence-types/type-step-end:
en: 7fc233c4a569dae2
zh: bc2b3fed4a397c00
@@ -2127,11 +2151,11 @@
en: 1b045de96172be4b
zh: 8a15c6f35b576b37
/session-persistence-event-catalog/resolved-persistence-types/type-usage:
en: 8eec3bb6eee5085b
zh: 04bb3281fd7bd95c
en: ebb801c4444061fb
zh: f2d6531de88ee16e
/session-persistence-event-catalog/resolved-persistence-types/type-user-message:
en: d6d9401f19a66ee8
zh: 569fdddf887660a2
en: 536f875df13987d0
zh: 857a381433a4217f
/session-persistence-event-catalog/resolved-persistence-types/type-web-deepseek-search-llm-request:
en: 99c8837344178a47
zh: bf746f5678346596
+146 -46
View File
@@ -23,7 +23,7 @@ The [format references](persistence-changes/historical-formats/README.md) cover
| `JsonlHeaderLine` | header | `18ee62b8900a4c3d046700f05d7a4d49d6cab2a660a020c87481d1603dd8bd4f` | [`HeaderLine`](#persistence-type-sha256-18ee62b8900a4c3d046700f05d7a4d49d6cab2a660a020c87481d1603dd8bd4f) |
| `SessionEventEnvelope` | envelope | `5776e5553ff2dfe3f5bc202dbb1e7c9f93e35a531aebb7764c23b2b6153b2ccc` | [`union (3 variants)`](#persistence-type-sha256-5776e5553ff2dfe3f5bc202dbb1e7c9f93e35a531aebb7764c23b2b6153b2ccc) |
| `event:agent-preset/selected` | event | `a10c17474eaf2ddab7095a099e0fe3d046fc18e56c3e344fc8894c05ff9ef97b` | [`{ type: "agent-preset/selected" }`](#persistence-type-sha256-a10c17474eaf2ddab7095a099e0fe3d046fc18e56c3e344fc8894c05ff9ef97b) |
| `event:agent/inbox/spliced` | event | `0755b7d1128319ba2e389619cbbf2c961ad648882b4e91da7f3e9582cc8a2034` | [`{ type: "agent/inbox/spliced" }`](#persistence-type-sha256-0755b7d1128319ba2e389619cbbf2c961ad648882b4e91da7f3e9582cc8a2034) |
| `event:agent/inbox/spliced` | event | `b36fd842cd004b532ce96fd5d6f5be28e017eda043131aa3cf5e69edc436108e` | [`{ type: "agent/inbox/spliced" }`](#persistence-type-sha256-b36fd842cd004b532ce96fd5d6f5be28e017eda043131aa3cf5e69edc436108e) |
| `event:approval/asked` | event | `3bfeb47b58606f4661904bc723da612782214c463d01e6d61cd6d6193d7374e1` | [`{ type: "approval/asked" }`](#persistence-type-sha256-3bfeb47b58606f4661904bc723da612782214c463d01e6d61cd6d6193d7374e1) |
| `event:approval/decided` | event | `bb1ab3d08f49a9f3b265f844cd78d5c49813062a7b34b54904b426f85d0ff6e3` | [`{ type: "approval/decided" }`](#persistence-type-sha256-bb1ab3d08f49a9f3b265f844cd78d5c49813062a7b34b54904b426f85d0ff6e3) |
| `event:approval/policy` | event | `26718e15e7e395bce9642dba5bbe09b3b1a4ce2213d20d566cd9207d7fc5fb78` | [`{ type: "approval/policy" }`](#persistence-type-sha256-26718e15e7e395bce9642dba5bbe09b3b1a4ce2213d20d566cd9207d7fc5fb78) |
@@ -36,7 +36,7 @@ The [format references](persistence-changes/historical-formats/README.md) cover
| `event:compaction/start` | event | `db874d463b0fdec77e9da1c4568f37cb70bd6596781eb93800db44fb8a116965` | [`{ type: "compaction/start" }`](#persistence-type-sha256-db874d463b0fdec77e9da1c4568f37cb70bd6596781eb93800db44fb8a116965) |
| `event:compaction/summary` | event | `e2f9a41e0989f54ed8cee80f8db2bcf9d60a5c810dc9d45b83fa050b9dce7602` | [`{ type: "compaction/summary" }`](#persistence-type-sha256-e2f9a41e0989f54ed8cee80f8db2bcf9d60a5c810dc9d45b83fa050b9dce7602) |
| `event:deliverables/presented` | event | `13d3d180f977bf78081d487ffa0ecb75857349bcab29a5a3fb48189fca2a6176` | [`{ type: "deliverables/presented" }`](#persistence-type-sha256-13d3d180f977bf78081d487ffa0ecb75857349bcab29a5a3fb48189fca2a6176) |
| `event:developer/message` | event | `97e0162f983c27456ec71269fb4976b1851588b8c1aeac5d071d43e11f95e03c` | [`{ type: "developer/message" }`](#persistence-type-sha256-97e0162f983c27456ec71269fb4976b1851588b8c1aeac5d071d43e11f95e03c) |
| `event:developer/message` | event | `5ef58f2a1bdc576c1f12fa1f4da1920ece020b13c0f991a578cddcbeb28bc5be` | [`{ type: "developer/message" }`](#persistence-type-sha256-5ef58f2a1bdc576c1f12fa1f4da1920ece020b13c0f991a578cddcbeb28bc5be) |
| `event:feedback/message-delete` | event | `3ee93b06f3a125850337602bcdf155d2538c43a5c944ec55b1b3c365152d6796` | [`{ type: "feedback/message-delete" }`](#persistence-type-sha256-3ee93b06f3a125850337602bcdf155d2538c43a5c944ec55b1b3c365152d6796) |
| `event:feedback/message-put` | event | `b5086d249e8502e9ead1d39156bb8d559bde7951cac0f14ce150345b4e42a2bf` | [`{ type: "feedback/message-put" }`](#persistence-type-sha256-b5086d249e8502e9ead1d39156bb8d559bde7951cac0f14ce150345b4e42a2bf) |
| `event:feedback/record` | event | `b54940ff095c17e874c5be03815f4c2145a256cf3a1d34dae4ab2f7769dfffe8` | [`{ type: "feedback/record" }`](#persistence-type-sha256-b54940ff095c17e874c5be03815f4c2145a256cf3a1d34dae4ab2f7769dfffe8) |
@@ -58,7 +58,7 @@ The [format references](persistence-changes/historical-formats/README.md) cover
| `event:session-log-deepseek/delivery-accepted` | event | `d63b8b8ffad9c02fd80c43a17df4f240c1fe8118ecca9de34f9d5871838ab5b9` | [`{ type: "session-log-deepseek/delivery-accepted" }`](#persistence-type-sha256-d63b8b8ffad9c02fd80c43a17df4f240c1fe8118ecca9de34f9d5871838ab5b9) |
| `event:session/end-seed` | event | `5e6db6e24948d4a853c71cb9fabd252ad051ce93d4672c1266cf837c1c17b84e` | [`{ type: "session/end-seed" }`](#persistence-type-sha256-5e6db6e24948d4a853c71cb9fabd252ad051ce93d4672c1266cf837c1c17b84e) |
| `event:session/title` | event | `1b912703e2d64f91c99c675b8f805b01076c8325b905c1218ad81ef0b24909d5` | [`{ type: "session/title" }`](#persistence-type-sha256-1b912703e2d64f91c99c675b8f805b01076c8325b905c1218ad81ef0b24909d5) |
| `event:session/title-llm-request` | event | `79a4c87f8bfb4d6d05cd9952b99210d6a7db3b478c4a01d94b4c50602308c056` | [`{ type: "session/title-llm-request" }`](#persistence-type-sha256-79a4c87f8bfb4d6d05cd9952b99210d6a7db3b478c4a01d94b4c50602308c056) |
| `event:session/title-llm-request` | event | `efa1a091017ce533e38b0ef3e14d11a5c102ff1793229b18dad719a4268d9adc` | [`{ type: "session/title-llm-request" }`](#persistence-type-sha256-efa1a091017ce533e38b0ef3e14d11a5c102ff1793229b18dad719a4268d9adc) |
| `event:step/end` | event | `e0a787e6ec76c7c94fecbc501b489164ab0293db05bc947914077ad01e674f05` | [`{ type: "step/end" }`](#persistence-type-sha256-e0a787e6ec76c7c94fecbc501b489164ab0293db05bc947914077ad01e674f05) |
| `event:step/start` | event | `4513e088d43e6c68425be30451b9f961cc264fe7318ca62681c41d4d78615986` | [`{ type: "step/start" }`](#persistence-type-sha256-4513e088d43e6c68425be30451b9f961cc264fe7318ca62681c41d4d78615986) |
| `event:subagent/catalog` | event | `53750b9abf61efb7992b0aadc3e247ec927449c63266df28230fd1139f5590ac` | [`{ type: "subagent/catalog" }`](#persistence-type-sha256-53750b9abf61efb7992b0aadc3e247ec927449c63266df28230fd1139f5590ac) |
@@ -80,7 +80,7 @@ The [format references](persistence-changes/historical-formats/README.md) cover
| `event:tool/result` | event | `7c9f44e90a0058f4cc532ae20dad0c10afa6eba22e70a6c79fc79490bad64397` | [`{ type: "tool/result" }`](#persistence-type-sha256-7c9f44e90a0058f4cc532ae20dad0c10afa6eba22e70a6c79fc79490bad64397) |
| `event:turn/end` | event | `0f8512903d94f57a4748fa1a2092e64342856796684e6b8343db685b192745ce` | [`{ type: "turn/end" }`](#persistence-type-sha256-0f8512903d94f57a4748fa1a2092e64342856796684e6b8343db685b192745ce) |
| `event:turn/start` | event | `aa0957eca50aeb28bcd2e6930b95809926edacb550c8c340ba526ba6b861b3d8` | [`{ type: "turn/start" }`](#persistence-type-sha256-aa0957eca50aeb28bcd2e6930b95809926edacb550c8c340ba526ba6b861b3d8) |
| `event:user/message` | event | `2ae8594a998c9862a597129e782277bcc518266712a703b5c3161bdf552d13b8` | [`{ type: "user/message" }`](#persistence-type-sha256-2ae8594a998c9862a597129e782277bcc518266712a703b5c3161bdf552d13b8) |
| `event:user/message` | event | `58b122df513aeaf7ddc5783dbb1fd844e2979bb273964e30ae351a8db6510019` | [`{ type: "user/message" }`](#persistence-type-sha256-58b122df513aeaf7ddc5783dbb1fd844e2979bb273964e30ae351a8db6510019) |
| `event:web/deepseek-search-llm-request` | event | `0c556c9b9f2a96238a6b9bbb99e748206cbff2ed6203041c9571f04ae5802f52` | [`{ type: "web/deepseek-search-llm-request" }`](#persistence-type-sha256-0c556c9b9f2a96238a6b9bbb99e748206cbff2ed6203041c9571f04ae5802f52) |
| `event:working-directory/change` | event | `124aa62078ca3311353b39cc3bf6c75da281338be6bbf672f8b361d828cddca3` | [`{ type: "working-directory/change" }`](#persistence-type-sha256-124aa62078ca3311353b39cc3bf6c75da281338be6bbf672f8b361d828cddca3) |
| `event:workspace/changes` | event | `e308ccf867a5398e316e0af8cb6ce238a8d33a63b9b384c8250a686786285f72` | [`{ type: "workspace/changes" }`](#persistence-type-sha256-e308ccf867a5398e316e0af8cb6ce238a8d33a63b9b384c8250a686786285f72) |
@@ -1347,6 +1347,14 @@ SHA-256: `1e0b0b66b6f0c53fc72e52289d06b4708d776f1ca9df4c86a22bff621d403cf0`
`"aborted"`
<a id="persistence-type-sha256-05205074b24d8340cfd7995f6cb64779c7f1241490954700115601e264178877"></a>
### `"action-cards"`
SHA-256: `05205074b24d8340cfd7995f6cb64779c7f1241490954700115601e264178877`
`"action-cards"`
<a id="persistence-type-sha256-c8e6f48878cc2a2f0dc59682280d81f407943fb503f32c64bf9ff3d9393c3eff"></a>
### `"active"`
@@ -3365,7 +3373,7 @@ Sources: [`packages/web/web-search-deepseek/src/provider.ts:55`](../packages/web
| `body` | required | [`{ max_tokens, messages, model, thinking?, … }`](#persistence-type-sha256-9c001ecb5e1d3c3894410db2a1e383842e5f8454fb52182338686158abc1b3ee) |
| `endpoint` | required | `string` |
<a id="persistence-type-sha256-0f6265188dae7ba6faa26830a180bb4deb0760561f97ee0c9d399ccf6c9cf1f7"></a>
<a id="persistence-type-sha256-7580e7d96dc8475fffe813a42af499665de3d4517fcdb3bc2337d0340e98d616"></a>
<a id="persistence-type-developermessage"></a>
@@ -3373,7 +3381,7 @@ Sources: [`packages/web/web-search-deepseek/src/provider.ts:55`](../packages/web
### `DeveloperMessage`
SHA-256: `0f6265188dae7ba6faa26830a180bb4deb0760561f97ee0c9d399ccf6c9cf1f7`
SHA-256: `7580e7d96dc8475fffe813a42af499665de3d4517fcdb3bc2337d0340e98d616`
Sources: [`packages/llm/llm/src/message.ts:157`](../packages/llm/llm/src/message.ts)
@@ -3382,11 +3390,11 @@ Sources: [`packages/llm/llm/src/message.ts:157`](../packages/llm/llm/src/message
| `content` | required | [`ContentBlock[]`](#persistence-type-sha256-b52f215a6cd691058a3413c12f161fa3b563a2f4895adb3df6f744ec4b0a6ef1) |
| `id` | required | `string` |
| `role` | required | `"developer"` |
| `source` | required | [`MessageSource`](#persistence-type-sha256-5bc2d7f02503fc6b3e1bb7e0b7fa22c0b9e4b9841fa3175b2ad8e607ae3525a8) |
| `source` | required | [`MessageSource`](#persistence-type-sha256-7a5b68bd1a5118c827df9e2466acc5e4d16b83e3f97a2304d992958647592690) |
Source compatibility: `source` — `session-source-attribution` v1; `session.developer-message.source`; `kind`; `preserve`.
Attribution-only additions: `tmux-context`, `user-question-reply`, `working-directory`.
Attribution-only additions: `action-cards`, `tmux-context`, `user-question-reply`, `working-directory`.
<a id="persistence-type-sha256-9a930283b91d493d6347146f2475bb107294b379415ca02162354f46a64164f7"></a>
@@ -4081,7 +4089,7 @@ Sources: [`packages/llm/llm-retry/src/types.ts:43`](../packages/llm/llm-retry/sr
| `step` | required | `number` |
| `turn` | required | `number` |
<a id="persistence-type-sha256-040cd65423bd64264d225504f1c572d239eeb1793d0c2856ad8e42bb6e1e148b"></a>
<a id="persistence-type-sha256-cda79b839d8cc0c7a7bd09dd067de04925f6e9037080d878b65aaa5a383f117c"></a>
<a id="persistence-type-message"></a>
@@ -4089,17 +4097,17 @@ Sources: [`packages/llm/llm-retry/src/types.ts:43`](../packages/llm/llm-retry/sr
### `Message`
SHA-256: `040cd65423bd64264d225504f1c572d239eeb1793d0c2856ad8e42bb6e1e148b`
SHA-256: `cda79b839d8cc0c7a7bd09dd067de04925f6e9037080d878b65aaa5a383f117c`
Sources: [`packages/llm/llm/src/message.ts:196`](../packages/llm/llm/src/message.ts)
One of:
- [`ToolResultMessage`](#persistence-type-sha256-4b259f7f8f0a44ad5e90b70a73553ced1df27012d126921d397ff403b6c86731)
- [`DeveloperMessage`](#persistence-type-sha256-0f6265188dae7ba6faa26830a180bb4deb0760561f97ee0c9d399ccf6c9cf1f7)
- [`UserMessage`](#persistence-type-sha256-2cf075b013d363ad743b5a4982900678d108499c721bc11255137402003db711)
- [`AssistantMessage`](#persistence-type-sha256-fa87537f4a9c1e25602990a8bf686b383e9e00352ec8946e16ae24ba7c1625be)
- [`DeveloperMessage`](#persistence-type-sha256-7580e7d96dc8475fffe813a42af499665de3d4517fcdb3bc2337d0340e98d616)
- [`UserMessage`](#persistence-type-sha256-b782058cef83caf3eb44a16adeef5657e4d5cdff2d50655aba418c4490670a69)
- [`SystemMessage`](#persistence-type-sha256-09f3f8dc417f278257202714132f98ba7b52957d7b558c4a04456603890eb363)
- [`AssistantMessage`](#persistence-type-sha256-fa87537f4a9c1e25602990a8bf686b383e9e00352ec8946e16ae24ba7c1625be)
<a id="persistence-type-sha256-7c6129b6961cf4b2a030f36c78d2b0a5be38617651a9b81322e054ee512bee06"></a>
@@ -4174,7 +4182,7 @@ One of:
- `"negative"`
- `"positive"`
<a id="persistence-type-sha256-5bc2d7f02503fc6b3e1bb7e0b7fa22c0b9e4b9841fa3175b2ad8e607ae3525a8"></a>
<a id="persistence-type-sha256-7a5b68bd1a5118c827df9e2466acc5e4d16b83e3f97a2304d992958647592690"></a>
<a id="persistence-type-messagesource"></a>
@@ -4182,12 +4190,19 @@ One of:
### `MessageSource`
SHA-256: `5bc2d7f02503fc6b3e1bb7e0b7fa22c0b9e4b9841fa3175b2ad8e607ae3525a8`
SHA-256: `7a5b68bd1a5118c827df9e2466acc5e4d16b83e3f97a2304d992958647592690`
Sources: [`packages/llm/llm/src/message.ts:136`](../packages/llm/llm/src/message.ts)
| kind | Form property | Other required fields | Full definition |
|---|---|---|---|
| `action-cards` | `"catalog"` | `through`: `number` | [`{ kind: "action-cards", form: "catalog" }`](#persistence-type-sha256-dd00cdccd0ba487b92362ef67db58cd264f48c1bfd368291424a1da9a899489a) |
| `action-cards` | `"instructions"` | `through`: `number` | [`{ kind: "action-cards", form: "instructions" }`](#persistence-type-sha256-ba2a1a8f194b1bb21f270713398f6643e206e8ff5f7f26f6d36a88e3f0f0ab10) |
| `action-cards` | `"notice"` | `summary`: `string`; `through`: `number` | [`{ kind: "action-cards", form: "notice" }`](#persistence-type-sha256-e7f4cbb3a57f4cbdb61ea41e95bcba626ad1fff6bb9b1e063b1d50a32da6e864) |
| `action-cards` | `"recall"` | `through`: `number` | [`{ kind: "action-cards", form: "recall" }`](#persistence-type-sha256-b613f83572fe2fbf7259c72308cafd008f4fceea897749128a48bcd158f290aa) |
| `action-cards` | `"relay"` | `through`: `number` | [`{ kind: "action-cards", form: "relay" }`](#persistence-type-sha256-45f005e6bbb7645248be792b715a74bc7fdf5a67f5381655d3a75dcc7aa2112e) |
| `action-cards` | `"snapshot"` | `sections`: [`ContextSnapshotSection[]`](#persistence-type-sha256-c748fb42c80a120038a8c587af80ec39924803c401364db11c8974d5bf6d0262); `through`: `number` | [`{ kind: "action-cards", form: "snapshot" }`](#persistence-type-sha256-3247e566f4340029325b70e4366b56fa9719977f1e39033b769a2ec221ab22a9) |
| `action-cards` | not declared | `through`: `number` | [`{ kind: "action-cards" }`](#persistence-type-sha256-dde0cdf9d426768b649b5a9783d2144e7f900c35ef65229cd89036dbf411d1b0) |
| `agent-instructions` | `"instructions"` | `changes`: [`AgentInstructionChange[]`](#persistence-type-sha256-2ab12a0e345658f9342816d773fe610f049982bcad82631f43b9205abe43e5ee) | [`AgentInstructionSource`](#persistence-type-sha256-6f22a9d02b9a1f1157f3b0e7bdccd7e7b12b085c4efdb709dfc74a465cd4e590) |
| `agent-message` | `"relay"` | `senderSessionId`: `string` | [`AgentMessageSource`](#persistence-type-sha256-4a9bef9f27a942520cc2279ca7dd3082556e4bff543f66b5c0333e1bbed1cd86) |
| `compact-checkpoint` | not declared | `compactionId`: `string` | [`CompactionCheckpointSource`](#persistence-type-sha256-31907de9563751822af6d9be02bafaf26faa098bbd1cb412524134d577cc1648) |
@@ -4300,13 +4315,13 @@ Sources: [`packages/llm/llm/src/message.ts:136`](../packages/llm/llm/src/message
| `webhook` | `"notice"` | `deliveryId`: `string`; `provider`: `string`; `ruleId`: `string`; `source`: `string`; `summary`: `string` | [`{ kind: "webhook", form: "notice" }`](#persistence-type-sha256-9edc162949abf93bd5ff049191b345454ba104f583937ce5580fb50f067d2b94) |
| `working-directory` | not declared | none | [`{ kind: "working-directory" }`](#persistence-type-sha256-2235d957bf4b7c6cf959231d480f0ba1f3a6d72d82981493fb6c7c963021d3d5) |
<a id="persistence-type-sha256-a927d444c928ac0efd9dd8b2e7d7d04d711c264cb71578d81e0e2fd143ff669f"></a>
<a id="persistence-type-sha256-78b9204976ab9faaf133c3488b73d1dfb6f1a4bb6c0903e3473cd41a5e1e39e1"></a>
### `Message[]`
SHA-256: `a927d444c928ac0efd9dd8b2e7d7d04d711c264cb71578d81e0e2fd143ff669f`
SHA-256: `78b9204976ab9faaf133c3488b73d1dfb6f1a4bb6c0903e3473cd41a5e1e39e1`
Array of [`Message`](#persistence-type-sha256-040cd65423bd64264d225504f1c572d239eeb1793d0c2856ad8e42bb6e1e148b).
Array of [`Message`](#persistence-type-sha256-cda79b839d8cc0c7a7bd09dd067de04925f6e9037080d878b65aaa5a383f117c).
<a id="persistence-type-sha256-719c51f67074f97034a6c94297fe1f4b5d3c8d392c57586dab732aa5398c7a91"></a>
@@ -4690,7 +4705,7 @@ Sources: [`packages/session/session-title/src/types.ts:41`](../packages/session/
| `source` | required | [`SessionTitleSource`](#persistence-type-sha256-30d5a537b296685770b1241c4b4bbde19d5a31ff81aa23df73fab5e90b8fc65f) |
| `title` | required | `string` |
<a id="persistence-type-sha256-00c054d3018c198d92f459563331edd1385e58345c677bb499791df2da0ade9f"></a>
<a id="persistence-type-sha256-a3e299483348f27a4787a36bb3f29cd86024c65336abc10bb3d687468302c4cf"></a>
<a id="persistence-type-packagessessionsession-title-llmsrcindextssessiontitlellmrequesteventdata"></a>
@@ -4698,7 +4713,7 @@ Sources: [`packages/session/session-title/src/types.ts:41`](../packages/session/
### `SessionTitleLlmRequestEventData`
SHA-256: `00c054d3018c198d92f459563331edd1385e58345c677bb499791df2da0ade9f`
SHA-256: `a3e299483348f27a4787a36bb3f29cd86024c65336abc10bb3d687468302c4cf`
Sources: [`packages/session/session-title-llm/src/index.ts:37`](../packages/session/session-title-llm/src/index.ts)
@@ -4706,7 +4721,7 @@ Sources: [`packages/session/session-title-llm/src/index.ts:37`](../packages/sess
|---|---|---|
| `maxTokens` | required | `number` |
| `messageSeqs` | required | [`number[]`](#persistence-type-sha256-5d03ba38734809bcbd2a55221bd938b3b3fc34b49bb686c6113e9e4931e3aa78) |
| `messages` | required | [`Message[]`](#persistence-type-sha256-a927d444c928ac0efd9dd8b2e7d7d04d711c264cb71578d81e0e2fd143ff669f) |
| `messages` | required | [`Message[]`](#persistence-type-sha256-78b9204976ab9faaf133c3488b73d1dfb6f1a4bb6c0903e3473cd41a5e1e39e1) |
| `reasoningEffort` | optional | `string` |
| `route` | required | [`SessionTitleModelIdentity`](#persistence-type-sha256-07e1a58c58b593f507b8e7f25723bb0aa42baae6e00f5ecfddf28cb1e64d3235) |
| `system` | required | `string` |
@@ -5362,7 +5377,7 @@ Sources: [`packages/core/session/src/types.ts:234`](../packages/core/session/src
| `interrupted` | not declared | none | [`{ kind: "interrupted" }`](#persistence-type-sha256-56b7c047694875a843ba020dfea3c8380d54ae4ff88ab349e72004dc21e07c23) |
| `max-tokens` | not declared | none | [`{ kind: "max-tokens" }`](#persistence-type-sha256-ab7ec5f9eb2c6dfacca5da4c37cf7ba85d04172053ee6b1ebf1709ffcd99c1a7) |
<a id="persistence-type-sha256-2cf075b013d363ad743b5a4982900678d108499c721bc11255137402003db711"></a>
<a id="persistence-type-sha256-b782058cef83caf3eb44a16adeef5657e4d5cdff2d50655aba418c4490670a69"></a>
<a id="persistence-type-packagesllmllmsrcmessagetsusermessage"></a>
@@ -5370,7 +5385,7 @@ Sources: [`packages/core/session/src/types.ts:234`](../packages/core/session/src
### `UserMessage`
SHA-256: `2cf075b013d363ad743b5a4982900678d108499c721bc11255137402003db711`
SHA-256: `b782058cef83caf3eb44a16adeef5657e4d5cdff2d50655aba418c4490670a69`
Sources: [`packages/llm/llm/src/message.ts:162`](../packages/llm/llm/src/message.ts)
@@ -5379,19 +5394,19 @@ Sources: [`packages/llm/llm/src/message.ts:162`](../packages/llm/llm/src/message
| `content` | required | [`ContentBlock[]`](#persistence-type-sha256-b52f215a6cd691058a3413c12f161fa3b563a2f4895adb3df6f744ec4b0a6ef1) |
| `id` | required | `string` |
| `role` | required | `"user"` |
| `source` | required | [`MessageSource`](#persistence-type-sha256-5bc2d7f02503fc6b3e1bb7e0b7fa22c0b9e4b9841fa3175b2ad8e607ae3525a8) |
| `source` | required | [`MessageSource`](#persistence-type-sha256-7a5b68bd1a5118c827df9e2466acc5e4d16b83e3f97a2304d992958647592690) |
Source compatibility: `source` — `session-source-attribution` v1; `session.user-message.source`; `kind`; `preserve`.
Attribution-only additions: `tmux-context`, `user-question-reply`, `working-directory`.
Attribution-only additions: `action-cards`, `tmux-context`, `user-question-reply`, `working-directory`.
<a id="persistence-type-sha256-454a0fa879e38076bdcdbd87ef561cd67d7e43e58a287cf7dbaa7b6cf7847020"></a>
<a id="persistence-type-sha256-5d00abf938272f22eb47199e9d40801e8d8ec4c88e2b138b15aa2a5a5bf87520"></a>
### `UserMessage[]`
SHA-256: `454a0fa879e38076bdcdbd87ef561cd67d7e43e58a287cf7dbaa7b6cf7847020`
SHA-256: `5d00abf938272f22eb47199e9d40801e8d8ec4c88e2b138b15aa2a5a5bf87520`
Array of [`UserMessage`](#persistence-type-sha256-2cf075b013d363ad743b5a4982900678d108499c721bc11255137402003db711).
Array of [`UserMessage`](#persistence-type-sha256-b782058cef83caf3eb44a16adeef5657e4d5cdff2d50655aba418c4490670a69).
<a id="persistence-type-sha256-c2deb7c9183735d4e0fffddf5c623f4e34daa1c8e96b111982e957dfcbbcf896"></a>
@@ -6276,18 +6291,18 @@ Sources: [`packages/core/session/src/types.ts:392`](../packages/core/session/src
| `reason` | required | [`RequestHeaderReason`](#persistence-type-sha256-530da6c0267813bb4511af72883cc518834e624e3b3220a7062955fd57a788a6) |
| `startsSeries` | optional | `true` |
<a id="persistence-type-sha256-f4ed4717b4c259e933b550a8dcd90dc93628cda8a3c7affcaa53b9c13c1d53eb"></a>
<a id="persistence-type-sha256-7404c6123fdaa591474a37979a5a8ae70d1387af7c9f09bfe568b218935a974d"></a>
### `{ headerSeq?, message, step, turn }`
SHA-256: `f4ed4717b4c259e933b550a8dcd90dc93628cda8a3c7affcaa53b9c13c1d53eb`
SHA-256: `7404c6123fdaa591474a37979a5a8ae70d1387af7c9f09bfe568b218935a974d`
Sources: [`packages/core/session/src/types.ts:313`](../packages/core/session/src/types.ts)
| Property | Presence | Type |
|---|---|---|
| `headerSeq` | optional | `number` |
| `message` | required | [`DeveloperMessage`](#persistence-type-sha256-0f6265188dae7ba6faa26830a180bb4deb0760561f97ee0c9d399ccf6c9cf1f7) |
| `message` | required | [`DeveloperMessage`](#persistence-type-sha256-7580e7d96dc8475fffe813a42af499665de3d4517fcdb3bc2337d0340e98d616) |
| `step` | required | `number` |
| `turn` | required | `number` |
@@ -6371,17 +6386,17 @@ Sources: [`packages/core/session/src/types.ts:429`](../packages/core/session/src
|---|---|---|
| `inherited` | optional | `true` |
<a id="persistence-type-sha256-fa106c5b33b657e8ef16e8cbf39f3b7d44e5cbcdf4f9303d9db6410f28951e42"></a>
<a id="persistence-type-sha256-b2a948a5340a9341fa3bab99507c7090c5b0456de72ec28b5b157b8df3f1d640"></a>
### `{ inserted, outcome?, removedCount?, start, … }`
SHA-256: `fa106c5b33b657e8ef16e8cbf39f3b7d44e5cbcdf4f9303d9db6410f28951e42`
SHA-256: `b2a948a5340a9341fa3bab99507c7090c5b0456de72ec28b5b157b8df3f1d640`
Sources: [`packages/core/agent/src/types.ts:110`](../packages/core/agent/src/types.ts)
| Property | Presence | Type |
|---|---|---|
| `inserted` | required | [`UserMessage[]`](#persistence-type-sha256-454a0fa879e38076bdcdbd87ef561cd67d7e43e58a287cf7dbaa7b6cf7847020) |
| `inserted` | required | [`UserMessage[]`](#persistence-type-sha256-5d00abf938272f22eb47199e9d40801e8d8ec4c88e2b138b15aa2a5a5bf87520) |
| `outcome` | optional | `"canceled"` |
| `removedCount` | optional | `number` |
| `start` | required | `number` |
@@ -6417,6 +6432,91 @@ Sources: [`packages/core/session/src/types.ts:205`](../packages/core/session/src
| `kind` | required | `"aborted"` |
| `reason` | required | [`TurnEndCancelCause`](#persistence-type-sha256-1f8c126cfab3df812cd115cfc23e3256f0fa2383400f4ce91ecad313577e59b0) |
<a id="persistence-type-sha256-dde0cdf9d426768b649b5a9783d2144e7f900c35ef65229cd89036dbf411d1b0"></a>
### `{ kind: "action-cards" }`
SHA-256: `dde0cdf9d426768b649b5a9783d2144e7f900c35ef65229cd89036dbf411d1b0`
| Property | Presence | Type |
|---|---|---|
| `kind` | required | `"action-cards"` |
| `through` | required | `number` |
<a id="persistence-type-sha256-dd00cdccd0ba487b92362ef67db58cd264f48c1bfd368291424a1da9a899489a"></a>
### `{ kind: "action-cards", form: "catalog" }`
SHA-256: `dd00cdccd0ba487b92362ef67db58cd264f48c1bfd368291424a1da9a899489a`
| Property | Presence | Type |
|---|---|---|
| `form` | required | `"catalog"` |
| `kind` | required | `"action-cards"` |
| `through` | required | `number` |
<a id="persistence-type-sha256-ba2a1a8f194b1bb21f270713398f6643e206e8ff5f7f26f6d36a88e3f0f0ab10"></a>
### `{ kind: "action-cards", form: "instructions" }`
SHA-256: `ba2a1a8f194b1bb21f270713398f6643e206e8ff5f7f26f6d36a88e3f0f0ab10`
| Property | Presence | Type |
|---|---|---|
| `form` | required | `"instructions"` |
| `kind` | required | `"action-cards"` |
| `through` | required | `number` |
<a id="persistence-type-sha256-e7f4cbb3a57f4cbdb61ea41e95bcba626ad1fff6bb9b1e063b1d50a32da6e864"></a>
### `{ kind: "action-cards", form: "notice" }`
SHA-256: `e7f4cbb3a57f4cbdb61ea41e95bcba626ad1fff6bb9b1e063b1d50a32da6e864`
| Property | Presence | Type |
|---|---|---|
| `form` | required | `"notice"` |
| `kind` | required | `"action-cards"` |
| `summary` | required | `string` |
| `through` | required | `number` |
<a id="persistence-type-sha256-b613f83572fe2fbf7259c72308cafd008f4fceea897749128a48bcd158f290aa"></a>
### `{ kind: "action-cards", form: "recall" }`
SHA-256: `b613f83572fe2fbf7259c72308cafd008f4fceea897749128a48bcd158f290aa`
| Property | Presence | Type |
|---|---|---|
| `form` | required | `"recall"` |
| `kind` | required | `"action-cards"` |
| `through` | required | `number` |
<a id="persistence-type-sha256-45f005e6bbb7645248be792b715a74bc7fdf5a67f5381655d3a75dcc7aa2112e"></a>
### `{ kind: "action-cards", form: "relay" }`
SHA-256: `45f005e6bbb7645248be792b715a74bc7fdf5a67f5381655d3a75dcc7aa2112e`
| Property | Presence | Type |
|---|---|---|
| `form` | required | `"relay"` |
| `kind` | required | `"action-cards"` |
| `through` | required | `number` |
<a id="persistence-type-sha256-3247e566f4340029325b70e4366b56fa9719977f1e39033b769a2ec221ab22a9"></a>
### `{ kind: "action-cards", form: "snapshot" }`
SHA-256: `3247e566f4340029325b70e4366b56fa9719977f1e39033b769a2ec221ab22a9`
| Property | Presence | Type |
|---|---|---|
| `form` | required | `"snapshot"` |
| `kind` | required | `"action-cards"` |
| `sections` | required | [`ContextSnapshotSection[]`](#persistence-type-sha256-c748fb42c80a120038a8c587af80ec39924803c401364db11c8974d5bf6d0262) |
| `through` | required | `number` |
<a id="persistence-type-sha256-023a355b2be0d688fb6e4df8388dadb942761d46cfe96d0f499aae9631a159d4"></a>
### `{ kind: "blocked" }`
@@ -7943,17 +8043,17 @@ SHA-256: `a10c17474eaf2ddab7095a099e0fe3d046fc18e56c3e344fc8894c05ff9ef97b`
| `time` | required | `number` |
| `type` | required | `"agent-preset/selected"` |
<a id="persistence-type-sha256-0755b7d1128319ba2e389619cbbf2c961ad648882b4e91da7f3e9582cc8a2034"></a>
<a id="persistence-type-sha256-b36fd842cd004b532ce96fd5d6f5be28e017eda043131aa3cf5e69edc436108e"></a>
<a id="persistence-type-eventagentinboxspliced"></a>
### `{ type: "agent/inbox/spliced" }`
SHA-256: `0755b7d1128319ba2e389619cbbf2c961ad648882b4e91da7f3e9582cc8a2034`
SHA-256: `b36fd842cd004b532ce96fd5d6f5be28e017eda043131aa3cf5e69edc436108e`
| Property | Presence | Type |
|---|---|---|
| `data` | required | [`{ inserted, outcome?, removedCount?, start, … }`](#persistence-type-sha256-fa106c5b33b657e8ef16e8cbf39f3b7d44e5cbcdf4f9303d9db6410f28951e42) |
| `data` | required | [`{ inserted, outcome?, removedCount?, start, … }`](#persistence-type-sha256-b2a948a5340a9341fa3bab99507c7090c5b0456de72ec28b5b157b8df3f1d640) |
| `ignorable` | optional | `true` |
| `seq` | required | `number` |
| `time` | required | `number` |
@@ -8194,17 +8294,17 @@ SHA-256: `13d3d180f977bf78081d487ffa0ecb75857349bcab29a5a3fb48189fca2a6176`
| `time` | required | `number` |
| `type` | required | `"deliverables/presented"` |
<a id="persistence-type-sha256-97e0162f983c27456ec71269fb4976b1851588b8c1aeac5d071d43e11f95e03c"></a>
<a id="persistence-type-sha256-5ef58f2a1bdc576c1f12fa1f4da1920ece020b13c0f991a578cddcbeb28bc5be"></a>
<a id="persistence-type-eventdevelopermessage"></a>
### `{ type: "developer/message" }`
SHA-256: `97e0162f983c27456ec71269fb4976b1851588b8c1aeac5d071d43e11f95e03c`
SHA-256: `5ef58f2a1bdc576c1f12fa1f4da1920ece020b13c0f991a578cddcbeb28bc5be`
| Property | Presence | Type |
|---|---|---|
| `data` | required | [`{ headerSeq?, message, step, turn }`](#persistence-type-sha256-f4ed4717b4c259e933b550a8dcd90dc93628cda8a3c7affcaa53b9c13c1d53eb) |
| `data` | required | [`{ headerSeq?, message, step, turn }`](#persistence-type-sha256-7404c6123fdaa591474a37979a5a8ae70d1387af7c9f09bfe568b218935a974d) |
| `ignorable` | optional | `true` |
| `seq` | required | `number` |
| `sourceEventSeqs` | optional | [`number[]`](#persistence-type-sha256-5d03ba38734809bcbd2a55221bd938b3b3fc34b49bb686c6113e9e4931e3aa78) |
@@ -8604,17 +8704,17 @@ SHA-256: `1b912703e2d64f91c99c675b8f805b01076c8325b905c1218ad81ef0b24909d5`
| `time` | required | `number` |
| `type` | required | `"session/title"` |
<a id="persistence-type-sha256-79a4c87f8bfb4d6d05cd9952b99210d6a7db3b478c4a01d94b4c50602308c056"></a>
<a id="persistence-type-sha256-efa1a091017ce533e38b0ef3e14d11a5c102ff1793229b18dad719a4268d9adc"></a>
<a id="persistence-type-eventsessiontitle-llm-request"></a>
### `{ type: "session/title-llm-request" }`
SHA-256: `79a4c87f8bfb4d6d05cd9952b99210d6a7db3b478c4a01d94b4c50602308c056`
SHA-256: `efa1a091017ce533e38b0ef3e14d11a5c102ff1793229b18dad719a4268d9adc`
| Property | Presence | Type |
|---|---|---|
| `data` | required | [`SessionTitleLlmRequestEventData`](#persistence-type-sha256-00c054d3018c198d92f459563331edd1385e58345c677bb499791df2da0ade9f) |
| `data` | required | [`SessionTitleLlmRequestEventData`](#persistence-type-sha256-a3e299483348f27a4787a36bb3f29cd86024c65336abc10bb3d687468302c4cf) |
| `ignorable` | optional | `true` |
| `seq` | required | `number` |
| `time` | required | `number` |
@@ -9037,17 +9137,17 @@ Sources: [`packages/llm/llm/src/types.ts:476`](../packages/llm/llm/src/types.ts)
| `type` | required | `"usage"` |
| `usage` | required | [`TokenUsage`](#persistence-type-sha256-fec5442b9ee4122e10514d81d5a43648003fe8bfb7ee2ba50c0cdbdd383ed394) |
<a id="persistence-type-sha256-2ae8594a998c9862a597129e782277bcc518266712a703b5c3161bdf552d13b8"></a>
<a id="persistence-type-sha256-58b122df513aeaf7ddc5783dbb1fd844e2979bb273964e30ae351a8db6510019"></a>
<a id="persistence-type-eventusermessage"></a>
### `{ type: "user/message" }`
SHA-256: `2ae8594a998c9862a597129e782277bcc518266712a703b5c3161bdf552d13b8`
SHA-256: `58b122df513aeaf7ddc5783dbb1fd844e2979bb273964e30ae351a8db6510019`
| Property | Presence | Type |
|---|---|---|
| `data` | required | [`UserMessage`](#persistence-type-sha256-2cf075b013d363ad743b5a4982900678d108499c721bc11255137402003db711) |
| `data` | required | [`UserMessage`](#persistence-type-sha256-b782058cef83caf3eb44a16adeef5657e4d5cdff2d50655aba418c4490670a69) |
| `ignorable` | optional | `true` |
| `seq` | required | `number` |
| `sourceEventSeqs` | optional | [`number[]`](#persistence-type-sha256-5d03ba38734809bcbd2a55221bd938b3b3fc34b49bb686c6113e9e4931e3aa78) |
+146 -46
View File
@@ -25,7 +25,7 @@
| `JsonlHeaderLine` | header | `18ee62b8900a4c3d046700f05d7a4d49d6cab2a660a020c87481d1603dd8bd4f` | [`HeaderLine`](#persistence-type-sha256-18ee62b8900a4c3d046700f05d7a4d49d6cab2a660a020c87481d1603dd8bd4f) |
| `SessionEventEnvelope` | envelope | `5776e5553ff2dfe3f5bc202dbb1e7c9f93e35a531aebb7764c23b2b6153b2ccc` | [`union (3 variants)`](#persistence-type-sha256-5776e5553ff2dfe3f5bc202dbb1e7c9f93e35a531aebb7764c23b2b6153b2ccc) |
| `event:agent-preset/selected` | event | `a10c17474eaf2ddab7095a099e0fe3d046fc18e56c3e344fc8894c05ff9ef97b` | [`{ type: "agent-preset/selected" }`](#persistence-type-sha256-a10c17474eaf2ddab7095a099e0fe3d046fc18e56c3e344fc8894c05ff9ef97b) |
| `event:agent/inbox/spliced` | event | `0755b7d1128319ba2e389619cbbf2c961ad648882b4e91da7f3e9582cc8a2034` | [`{ type: "agent/inbox/spliced" }`](#persistence-type-sha256-0755b7d1128319ba2e389619cbbf2c961ad648882b4e91da7f3e9582cc8a2034) |
| `event:agent/inbox/spliced` | event | `b36fd842cd004b532ce96fd5d6f5be28e017eda043131aa3cf5e69edc436108e` | [`{ type: "agent/inbox/spliced" }`](#persistence-type-sha256-b36fd842cd004b532ce96fd5d6f5be28e017eda043131aa3cf5e69edc436108e) |
| `event:approval/asked` | event | `3bfeb47b58606f4661904bc723da612782214c463d01e6d61cd6d6193d7374e1` | [`{ type: "approval/asked" }`](#persistence-type-sha256-3bfeb47b58606f4661904bc723da612782214c463d01e6d61cd6d6193d7374e1) |
| `event:approval/decided` | event | `bb1ab3d08f49a9f3b265f844cd78d5c49813062a7b34b54904b426f85d0ff6e3` | [`{ type: "approval/decided" }`](#persistence-type-sha256-bb1ab3d08f49a9f3b265f844cd78d5c49813062a7b34b54904b426f85d0ff6e3) |
| `event:approval/policy` | event | `26718e15e7e395bce9642dba5bbe09b3b1a4ce2213d20d566cd9207d7fc5fb78` | [`{ type: "approval/policy" }`](#persistence-type-sha256-26718e15e7e395bce9642dba5bbe09b3b1a4ce2213d20d566cd9207d7fc5fb78) |
@@ -38,7 +38,7 @@
| `event:compaction/start` | event | `db874d463b0fdec77e9da1c4568f37cb70bd6596781eb93800db44fb8a116965` | [`{ type: "compaction/start" }`](#persistence-type-sha256-db874d463b0fdec77e9da1c4568f37cb70bd6596781eb93800db44fb8a116965) |
| `event:compaction/summary` | event | `e2f9a41e0989f54ed8cee80f8db2bcf9d60a5c810dc9d45b83fa050b9dce7602` | [`{ type: "compaction/summary" }`](#persistence-type-sha256-e2f9a41e0989f54ed8cee80f8db2bcf9d60a5c810dc9d45b83fa050b9dce7602) |
| `event:deliverables/presented` | event | `13d3d180f977bf78081d487ffa0ecb75857349bcab29a5a3fb48189fca2a6176` | [`{ type: "deliverables/presented" }`](#persistence-type-sha256-13d3d180f977bf78081d487ffa0ecb75857349bcab29a5a3fb48189fca2a6176) |
| `event:developer/message` | event | `97e0162f983c27456ec71269fb4976b1851588b8c1aeac5d071d43e11f95e03c` | [`{ type: "developer/message" }`](#persistence-type-sha256-97e0162f983c27456ec71269fb4976b1851588b8c1aeac5d071d43e11f95e03c) |
| `event:developer/message` | event | `5ef58f2a1bdc576c1f12fa1f4da1920ece020b13c0f991a578cddcbeb28bc5be` | [`{ type: "developer/message" }`](#persistence-type-sha256-5ef58f2a1bdc576c1f12fa1f4da1920ece020b13c0f991a578cddcbeb28bc5be) |
| `event:feedback/message-delete` | event | `3ee93b06f3a125850337602bcdf155d2538c43a5c944ec55b1b3c365152d6796` | [`{ type: "feedback/message-delete" }`](#persistence-type-sha256-3ee93b06f3a125850337602bcdf155d2538c43a5c944ec55b1b3c365152d6796) |
| `event:feedback/message-put` | event | `b5086d249e8502e9ead1d39156bb8d559bde7951cac0f14ce150345b4e42a2bf` | [`{ type: "feedback/message-put" }`](#persistence-type-sha256-b5086d249e8502e9ead1d39156bb8d559bde7951cac0f14ce150345b4e42a2bf) |
| `event:feedback/record` | event | `b54940ff095c17e874c5be03815f4c2145a256cf3a1d34dae4ab2f7769dfffe8` | [`{ type: "feedback/record" }`](#persistence-type-sha256-b54940ff095c17e874c5be03815f4c2145a256cf3a1d34dae4ab2f7769dfffe8) |
@@ -60,7 +60,7 @@
| `event:session-log-deepseek/delivery-accepted` | event | `d63b8b8ffad9c02fd80c43a17df4f240c1fe8118ecca9de34f9d5871838ab5b9` | [`{ type: "session-log-deepseek/delivery-accepted" }`](#persistence-type-sha256-d63b8b8ffad9c02fd80c43a17df4f240c1fe8118ecca9de34f9d5871838ab5b9) |
| `event:session/end-seed` | event | `5e6db6e24948d4a853c71cb9fabd252ad051ce93d4672c1266cf837c1c17b84e` | [`{ type: "session/end-seed" }`](#persistence-type-sha256-5e6db6e24948d4a853c71cb9fabd252ad051ce93d4672c1266cf837c1c17b84e) |
| `event:session/title` | event | `1b912703e2d64f91c99c675b8f805b01076c8325b905c1218ad81ef0b24909d5` | [`{ type: "session/title" }`](#persistence-type-sha256-1b912703e2d64f91c99c675b8f805b01076c8325b905c1218ad81ef0b24909d5) |
| `event:session/title-llm-request` | event | `79a4c87f8bfb4d6d05cd9952b99210d6a7db3b478c4a01d94b4c50602308c056` | [`{ type: "session/title-llm-request" }`](#persistence-type-sha256-79a4c87f8bfb4d6d05cd9952b99210d6a7db3b478c4a01d94b4c50602308c056) |
| `event:session/title-llm-request` | event | `efa1a091017ce533e38b0ef3e14d11a5c102ff1793229b18dad719a4268d9adc` | [`{ type: "session/title-llm-request" }`](#persistence-type-sha256-efa1a091017ce533e38b0ef3e14d11a5c102ff1793229b18dad719a4268d9adc) |
| `event:step/end` | event | `e0a787e6ec76c7c94fecbc501b489164ab0293db05bc947914077ad01e674f05` | [`{ type: "step/end" }`](#persistence-type-sha256-e0a787e6ec76c7c94fecbc501b489164ab0293db05bc947914077ad01e674f05) |
| `event:step/start` | event | `4513e088d43e6c68425be30451b9f961cc264fe7318ca62681c41d4d78615986` | [`{ type: "step/start" }`](#persistence-type-sha256-4513e088d43e6c68425be30451b9f961cc264fe7318ca62681c41d4d78615986) |
| `event:subagent/catalog` | event | `53750b9abf61efb7992b0aadc3e247ec927449c63266df28230fd1139f5590ac` | [`{ type: "subagent/catalog" }`](#persistence-type-sha256-53750b9abf61efb7992b0aadc3e247ec927449c63266df28230fd1139f5590ac) |
@@ -82,7 +82,7 @@
| `event:tool/result` | event | `7c9f44e90a0058f4cc532ae20dad0c10afa6eba22e70a6c79fc79490bad64397` | [`{ type: "tool/result" }`](#persistence-type-sha256-7c9f44e90a0058f4cc532ae20dad0c10afa6eba22e70a6c79fc79490bad64397) |
| `event:turn/end` | event | `0f8512903d94f57a4748fa1a2092e64342856796684e6b8343db685b192745ce` | [`{ type: "turn/end" }`](#persistence-type-sha256-0f8512903d94f57a4748fa1a2092e64342856796684e6b8343db685b192745ce) |
| `event:turn/start` | event | `aa0957eca50aeb28bcd2e6930b95809926edacb550c8c340ba526ba6b861b3d8` | [`{ type: "turn/start" }`](#persistence-type-sha256-aa0957eca50aeb28bcd2e6930b95809926edacb550c8c340ba526ba6b861b3d8) |
| `event:user/message` | event | `2ae8594a998c9862a597129e782277bcc518266712a703b5c3161bdf552d13b8` | [`{ type: "user/message" }`](#persistence-type-sha256-2ae8594a998c9862a597129e782277bcc518266712a703b5c3161bdf552d13b8) |
| `event:user/message` | event | `58b122df513aeaf7ddc5783dbb1fd844e2979bb273964e30ae351a8db6510019` | [`{ type: "user/message" }`](#persistence-type-sha256-58b122df513aeaf7ddc5783dbb1fd844e2979bb273964e30ae351a8db6510019) |
| `event:web/deepseek-search-llm-request` | event | `0c556c9b9f2a96238a6b9bbb99e748206cbff2ed6203041c9571f04ae5802f52` | [`{ type: "web/deepseek-search-llm-request" }`](#persistence-type-sha256-0c556c9b9f2a96238a6b9bbb99e748206cbff2ed6203041c9571f04ae5802f52) |
| `event:working-directory/change` | event | `124aa62078ca3311353b39cc3bf6c75da281338be6bbf672f8b361d828cddca3` | [`{ type: "working-directory/change" }`](#persistence-type-sha256-124aa62078ca3311353b39cc3bf6c75da281338be6bbf672f8b361d828cddca3) |
| `event:workspace/changes` | event | `e308ccf867a5398e316e0af8cb6ce238a8d33a63b9b384c8250a686786285f72` | [`{ type: "workspace/changes" }`](#persistence-type-sha256-e308ccf867a5398e316e0af8cb6ce238a8d33a63b9b384c8250a686786285f72) |
@@ -1349,6 +1349,14 @@ SHA-256: `1e0b0b66b6f0c53fc72e52289d06b4708d776f1ca9df4c86a22bff621d403cf0`
`"aborted"`
<a id="persistence-type-sha256-05205074b24d8340cfd7995f6cb64779c7f1241490954700115601e264178877"></a>
### `"action-cards"`
SHA-256: `05205074b24d8340cfd7995f6cb64779c7f1241490954700115601e264178877`
`"action-cards"`
<a id="persistence-type-sha256-c8e6f48878cc2a2f0dc59682280d81f407943fb503f32c64bf9ff3d9393c3eff"></a>
### `"active"`
@@ -3367,7 +3375,7 @@ SHA-256: `abd6ca62a5ab166553848324a23fef90eed7f59c8021bd63e7ea5bc6fd666b0c`
| `body` | 必需 | [`{ max_tokens, messages, model, thinking?, … }`](#persistence-type-sha256-9c001ecb5e1d3c3894410db2a1e383842e5f8454fb52182338686158abc1b3ee) |
| `endpoint` | 必需 | `string` |
<a id="persistence-type-sha256-0f6265188dae7ba6faa26830a180bb4deb0760561f97ee0c9d399ccf6c9cf1f7"></a>
<a id="persistence-type-sha256-7580e7d96dc8475fffe813a42af499665de3d4517fcdb3bc2337d0340e98d616"></a>
<a id="persistence-type-developermessage"></a>
@@ -3375,7 +3383,7 @@ SHA-256: `abd6ca62a5ab166553848324a23fef90eed7f59c8021bd63e7ea5bc6fd666b0c`
### `DeveloperMessage`
SHA-256: `0f6265188dae7ba6faa26830a180bb4deb0760561f97ee0c9d399ccf6c9cf1f7`
SHA-256: `7580e7d96dc8475fffe813a42af499665de3d4517fcdb3bc2337d0340e98d616`
来源:[`packages/llm/llm/src/message.ts:157`](../packages/llm/llm/src/message.ts)
@@ -3384,11 +3392,11 @@ SHA-256: `0f6265188dae7ba6faa26830a180bb4deb0760561f97ee0c9d399ccf6c9cf1f7`
| `content` | 必需 | [`ContentBlock[]`](#persistence-type-sha256-b52f215a6cd691058a3413c12f161fa3b563a2f4895adb3df6f744ec4b0a6ef1) |
| `id` | 必需 | `string` |
| `role` | 必需 | `"developer"` |
| `source` | 必需 | [`MessageSource`](#persistence-type-sha256-5bc2d7f02503fc6b3e1bb7e0b7fa22c0b9e4b9841fa3175b2ad8e607ae3525a8) |
| `source` | 必需 | [`MessageSource`](#persistence-type-sha256-7a5b68bd1a5118c827df9e2466acc5e4d16b83e3f97a2304d992958647592690) |
消息源兼容策略:`source` — `session-source-attribution` v1; `session.developer-message.source`; `kind`; `preserve`.
仅表示归属的新增 kind:`tmux-context`, `user-question-reply`, `working-directory`.
仅表示归属的新增 kind:`action-cards`, `tmux-context`, `user-question-reply`, `working-directory`.
<a id="persistence-type-sha256-9a930283b91d493d6347146f2475bb107294b379415ca02162354f46a64164f7"></a>
@@ -4083,7 +4091,7 @@ SHA-256: `6f72ece8f317cd080c4f20a93f1bf709965f5b81ac4669528f5a023191cfcf20`
| `step` | 必需 | `number` |
| `turn` | 必需 | `number` |
<a id="persistence-type-sha256-040cd65423bd64264d225504f1c572d239eeb1793d0c2856ad8e42bb6e1e148b"></a>
<a id="persistence-type-sha256-cda79b839d8cc0c7a7bd09dd067de04925f6e9037080d878b65aaa5a383f117c"></a>
<a id="persistence-type-message"></a>
@@ -4091,17 +4099,17 @@ SHA-256: `6f72ece8f317cd080c4f20a93f1bf709965f5b81ac4669528f5a023191cfcf20`
### `Message`
SHA-256: `040cd65423bd64264d225504f1c572d239eeb1793d0c2856ad8e42bb6e1e148b`
SHA-256: `cda79b839d8cc0c7a7bd09dd067de04925f6e9037080d878b65aaa5a383f117c`
来源:[`packages/llm/llm/src/message.ts:196`](../packages/llm/llm/src/message.ts)
以下类型之一:
- [`ToolResultMessage`](#persistence-type-sha256-4b259f7f8f0a44ad5e90b70a73553ced1df27012d126921d397ff403b6c86731)
- [`DeveloperMessage`](#persistence-type-sha256-0f6265188dae7ba6faa26830a180bb4deb0760561f97ee0c9d399ccf6c9cf1f7)
- [`UserMessage`](#persistence-type-sha256-2cf075b013d363ad743b5a4982900678d108499c721bc11255137402003db711)
- [`AssistantMessage`](#persistence-type-sha256-fa87537f4a9c1e25602990a8bf686b383e9e00352ec8946e16ae24ba7c1625be)
- [`DeveloperMessage`](#persistence-type-sha256-7580e7d96dc8475fffe813a42af499665de3d4517fcdb3bc2337d0340e98d616)
- [`UserMessage`](#persistence-type-sha256-b782058cef83caf3eb44a16adeef5657e4d5cdff2d50655aba418c4490670a69)
- [`SystemMessage`](#persistence-type-sha256-09f3f8dc417f278257202714132f98ba7b52957d7b558c4a04456603890eb363)
- [`AssistantMessage`](#persistence-type-sha256-fa87537f4a9c1e25602990a8bf686b383e9e00352ec8946e16ae24ba7c1625be)
<a id="persistence-type-sha256-7c6129b6961cf4b2a030f36c78d2b0a5be38617651a9b81322e054ee512bee06"></a>
@@ -4176,7 +4184,7 @@ SHA-256: `390eed1028aba02774f14408647acec10ec5516e97d4f15ecde2b9989d5a69f2`
- `"negative"`
- `"positive"`
<a id="persistence-type-sha256-5bc2d7f02503fc6b3e1bb7e0b7fa22c0b9e4b9841fa3175b2ad8e607ae3525a8"></a>
<a id="persistence-type-sha256-7a5b68bd1a5118c827df9e2466acc5e4d16b83e3f97a2304d992958647592690"></a>
<a id="persistence-type-messagesource"></a>
@@ -4184,12 +4192,19 @@ SHA-256: `390eed1028aba02774f14408647acec10ec5516e97d4f15ecde2b9989d5a69f2`
### `MessageSource`
SHA-256: `5bc2d7f02503fc6b3e1bb7e0b7fa22c0b9e4b9841fa3175b2ad8e607ae3525a8`
SHA-256: `7a5b68bd1a5118c827df9e2466acc5e4d16b83e3f97a2304d992958647592690`
来源:[`packages/llm/llm/src/message.ts:136`](../packages/llm/llm/src/message.ts)
| kind | form 属性 | 其他必需字段 | 完整定义 |
|---|---|---|---|
| `action-cards` | `"catalog"` | `through`: `number` | [`{ kind: "action-cards", form: "catalog" }`](#persistence-type-sha256-dd00cdccd0ba487b92362ef67db58cd264f48c1bfd368291424a1da9a899489a) |
| `action-cards` | `"instructions"` | `through`: `number` | [`{ kind: "action-cards", form: "instructions" }`](#persistence-type-sha256-ba2a1a8f194b1bb21f270713398f6643e206e8ff5f7f26f6d36a88e3f0f0ab10) |
| `action-cards` | `"notice"` | `summary`: `string`; `through`: `number` | [`{ kind: "action-cards", form: "notice" }`](#persistence-type-sha256-e7f4cbb3a57f4cbdb61ea41e95bcba626ad1fff6bb9b1e063b1d50a32da6e864) |
| `action-cards` | `"recall"` | `through`: `number` | [`{ kind: "action-cards", form: "recall" }`](#persistence-type-sha256-b613f83572fe2fbf7259c72308cafd008f4fceea897749128a48bcd158f290aa) |
| `action-cards` | `"relay"` | `through`: `number` | [`{ kind: "action-cards", form: "relay" }`](#persistence-type-sha256-45f005e6bbb7645248be792b715a74bc7fdf5a67f5381655d3a75dcc7aa2112e) |
| `action-cards` | `"snapshot"` | `sections`: [`ContextSnapshotSection[]`](#persistence-type-sha256-c748fb42c80a120038a8c587af80ec39924803c401364db11c8974d5bf6d0262); `through`: `number` | [`{ kind: "action-cards", form: "snapshot" }`](#persistence-type-sha256-3247e566f4340029325b70e4366b56fa9719977f1e39033b769a2ec221ab22a9) |
| `action-cards` | 未声明 | `through`: `number` | [`{ kind: "action-cards" }`](#persistence-type-sha256-dde0cdf9d426768b649b5a9783d2144e7f900c35ef65229cd89036dbf411d1b0) |
| `agent-instructions` | `"instructions"` | `changes`: [`AgentInstructionChange[]`](#persistence-type-sha256-2ab12a0e345658f9342816d773fe610f049982bcad82631f43b9205abe43e5ee) | [`AgentInstructionSource`](#persistence-type-sha256-6f22a9d02b9a1f1157f3b0e7bdccd7e7b12b085c4efdb709dfc74a465cd4e590) |
| `agent-message` | `"relay"` | `senderSessionId`: `string` | [`AgentMessageSource`](#persistence-type-sha256-4a9bef9f27a942520cc2279ca7dd3082556e4bff543f66b5c0333e1bbed1cd86) |
| `compact-checkpoint` | 未声明 | `compactionId`: `string` | [`CompactionCheckpointSource`](#persistence-type-sha256-31907de9563751822af6d9be02bafaf26faa098bbd1cb412524134d577cc1648) |
@@ -4302,13 +4317,13 @@ SHA-256: `5bc2d7f02503fc6b3e1bb7e0b7fa22c0b9e4b9841fa3175b2ad8e607ae3525a8`
| `webhook` | `"notice"` | `deliveryId`: `string`; `provider`: `string`; `ruleId`: `string`; `source`: `string`; `summary`: `string` | [`{ kind: "webhook", form: "notice" }`](#persistence-type-sha256-9edc162949abf93bd5ff049191b345454ba104f583937ce5580fb50f067d2b94) |
| `working-directory` | 未声明 | 无 | [`{ kind: "working-directory" }`](#persistence-type-sha256-2235d957bf4b7c6cf959231d480f0ba1f3a6d72d82981493fb6c7c963021d3d5) |
<a id="persistence-type-sha256-a927d444c928ac0efd9dd8b2e7d7d04d711c264cb71578d81e0e2fd143ff669f"></a>
<a id="persistence-type-sha256-78b9204976ab9faaf133c3488b73d1dfb6f1a4bb6c0903e3473cd41a5e1e39e1"></a>
### `Message[]`
SHA-256: `a927d444c928ac0efd9dd8b2e7d7d04d711c264cb71578d81e0e2fd143ff669f`
SHA-256: `78b9204976ab9faaf133c3488b73d1dfb6f1a4bb6c0903e3473cd41a5e1e39e1`
[`Message`](#persistence-type-sha256-040cd65423bd64264d225504f1c572d239eeb1793d0c2856ad8e42bb6e1e148b) 的数组。
[`Message`](#persistence-type-sha256-cda79b839d8cc0c7a7bd09dd067de04925f6e9037080d878b65aaa5a383f117c) 的数组。
<a id="persistence-type-sha256-719c51f67074f97034a6c94297fe1f4b5d3c8d392c57586dab732aa5398c7a91"></a>
@@ -4692,7 +4707,7 @@ SHA-256: `b56f6a885da3dc394c69a3e3cc5c0cfc4da15601b63fda4989eee8f40efc5be4`
| `source` | 必需 | [`SessionTitleSource`](#persistence-type-sha256-30d5a537b296685770b1241c4b4bbde19d5a31ff81aa23df73fab5e90b8fc65f) |
| `title` | 必需 | `string` |
<a id="persistence-type-sha256-00c054d3018c198d92f459563331edd1385e58345c677bb499791df2da0ade9f"></a>
<a id="persistence-type-sha256-a3e299483348f27a4787a36bb3f29cd86024c65336abc10bb3d687468302c4cf"></a>
<a id="persistence-type-packagessessionsession-title-llmsrcindextssessiontitlellmrequesteventdata"></a>
@@ -4700,7 +4715,7 @@ SHA-256: `b56f6a885da3dc394c69a3e3cc5c0cfc4da15601b63fda4989eee8f40efc5be4`
### `SessionTitleLlmRequestEventData`
SHA-256: `00c054d3018c198d92f459563331edd1385e58345c677bb499791df2da0ade9f`
SHA-256: `a3e299483348f27a4787a36bb3f29cd86024c65336abc10bb3d687468302c4cf`
来源:[`packages/session/session-title-llm/src/index.ts:37`](../packages/session/session-title-llm/src/index.ts)
@@ -4708,7 +4723,7 @@ SHA-256: `00c054d3018c198d92f459563331edd1385e58345c677bb499791df2da0ade9f`
|---|---|---|
| `maxTokens` | 必需 | `number` |
| `messageSeqs` | 必需 | [`number[]`](#persistence-type-sha256-5d03ba38734809bcbd2a55221bd938b3b3fc34b49bb686c6113e9e4931e3aa78) |
| `messages` | 必需 | [`Message[]`](#persistence-type-sha256-a927d444c928ac0efd9dd8b2e7d7d04d711c264cb71578d81e0e2fd143ff669f) |
| `messages` | 必需 | [`Message[]`](#persistence-type-sha256-78b9204976ab9faaf133c3488b73d1dfb6f1a4bb6c0903e3473cd41a5e1e39e1) |
| `reasoningEffort` | 可选 | `string` |
| `route` | 必需 | [`SessionTitleModelIdentity`](#persistence-type-sha256-07e1a58c58b593f507b8e7f25723bb0aa42baae6e00f5ecfddf28cb1e64d3235) |
| `system` | 必需 | `string` |
@@ -5364,7 +5379,7 @@ SHA-256: `f1687f982fa3d41ef29e159feed154ecd13af4dd209acaee16038f223f2e9402`
| `interrupted` | 未声明 | 无 | [`{ kind: "interrupted" }`](#persistence-type-sha256-56b7c047694875a843ba020dfea3c8380d54ae4ff88ab349e72004dc21e07c23) |
| `max-tokens` | 未声明 | 无 | [`{ kind: "max-tokens" }`](#persistence-type-sha256-ab7ec5f9eb2c6dfacca5da4c37cf7ba85d04172053ee6b1ebf1709ffcd99c1a7) |
<a id="persistence-type-sha256-2cf075b013d363ad743b5a4982900678d108499c721bc11255137402003db711"></a>
<a id="persistence-type-sha256-b782058cef83caf3eb44a16adeef5657e4d5cdff2d50655aba418c4490670a69"></a>
<a id="persistence-type-packagesllmllmsrcmessagetsusermessage"></a>
@@ -5372,7 +5387,7 @@ SHA-256: `f1687f982fa3d41ef29e159feed154ecd13af4dd209acaee16038f223f2e9402`
### `UserMessage`
SHA-256: `2cf075b013d363ad743b5a4982900678d108499c721bc11255137402003db711`
SHA-256: `b782058cef83caf3eb44a16adeef5657e4d5cdff2d50655aba418c4490670a69`
来源:[`packages/llm/llm/src/message.ts:162`](../packages/llm/llm/src/message.ts)
@@ -5381,19 +5396,19 @@ SHA-256: `2cf075b013d363ad743b5a4982900678d108499c721bc11255137402003db711`
| `content` | 必需 | [`ContentBlock[]`](#persistence-type-sha256-b52f215a6cd691058a3413c12f161fa3b563a2f4895adb3df6f744ec4b0a6ef1) |
| `id` | 必需 | `string` |
| `role` | 必需 | `"user"` |
| `source` | 必需 | [`MessageSource`](#persistence-type-sha256-5bc2d7f02503fc6b3e1bb7e0b7fa22c0b9e4b9841fa3175b2ad8e607ae3525a8) |
| `source` | 必需 | [`MessageSource`](#persistence-type-sha256-7a5b68bd1a5118c827df9e2466acc5e4d16b83e3f97a2304d992958647592690) |
消息源兼容策略:`source` — `session-source-attribution` v1; `session.user-message.source`; `kind`; `preserve`.
仅表示归属的新增 kind:`tmux-context`, `user-question-reply`, `working-directory`.
仅表示归属的新增 kind:`action-cards`, `tmux-context`, `user-question-reply`, `working-directory`.
<a id="persistence-type-sha256-454a0fa879e38076bdcdbd87ef561cd67d7e43e58a287cf7dbaa7b6cf7847020"></a>
<a id="persistence-type-sha256-5d00abf938272f22eb47199e9d40801e8d8ec4c88e2b138b15aa2a5a5bf87520"></a>
### `UserMessage[]`
SHA-256: `454a0fa879e38076bdcdbd87ef561cd67d7e43e58a287cf7dbaa7b6cf7847020`
SHA-256: `5d00abf938272f22eb47199e9d40801e8d8ec4c88e2b138b15aa2a5a5bf87520`
[`UserMessage`](#persistence-type-sha256-2cf075b013d363ad743b5a4982900678d108499c721bc11255137402003db711) 的数组。
[`UserMessage`](#persistence-type-sha256-b782058cef83caf3eb44a16adeef5657e4d5cdff2d50655aba418c4490670a69) 的数组。
<a id="persistence-type-sha256-c2deb7c9183735d4e0fffddf5c623f4e34daa1c8e96b111982e957dfcbbcf896"></a>
@@ -6278,18 +6293,18 @@ SHA-256: `ad0c38a0adb82a94c925456df41586d9b5f9202fca18593eae2213d4445b4585`
| `reason` | 必需 | [`RequestHeaderReason`](#persistence-type-sha256-530da6c0267813bb4511af72883cc518834e624e3b3220a7062955fd57a788a6) |
| `startsSeries` | 可选 | `true` |
<a id="persistence-type-sha256-f4ed4717b4c259e933b550a8dcd90dc93628cda8a3c7affcaa53b9c13c1d53eb"></a>
<a id="persistence-type-sha256-7404c6123fdaa591474a37979a5a8ae70d1387af7c9f09bfe568b218935a974d"></a>
### `{ headerSeq?, message, step, turn }`
SHA-256: `f4ed4717b4c259e933b550a8dcd90dc93628cda8a3c7affcaa53b9c13c1d53eb`
SHA-256: `7404c6123fdaa591474a37979a5a8ae70d1387af7c9f09bfe568b218935a974d`
来源:[`packages/core/session/src/types.ts:313`](../packages/core/session/src/types.ts)
| 属性 | 存在性 | 类型 |
|---|---|---|
| `headerSeq` | 可选 | `number` |
| `message` | 必需 | [`DeveloperMessage`](#persistence-type-sha256-0f6265188dae7ba6faa26830a180bb4deb0760561f97ee0c9d399ccf6c9cf1f7) |
| `message` | 必需 | [`DeveloperMessage`](#persistence-type-sha256-7580e7d96dc8475fffe813a42af499665de3d4517fcdb3bc2337d0340e98d616) |
| `step` | 必需 | `number` |
| `turn` | 必需 | `number` |
@@ -6373,17 +6388,17 @@ SHA-256: `17d1afb770d9941936130996da00dc86782cfef731d8d6526162c301256a4ac3`
|---|---|---|
| `inherited` | 可选 | `true` |
<a id="persistence-type-sha256-fa106c5b33b657e8ef16e8cbf39f3b7d44e5cbcdf4f9303d9db6410f28951e42"></a>
<a id="persistence-type-sha256-b2a948a5340a9341fa3bab99507c7090c5b0456de72ec28b5b157b8df3f1d640"></a>
### `{ inserted, outcome?, removedCount?, start, … }`
SHA-256: `fa106c5b33b657e8ef16e8cbf39f3b7d44e5cbcdf4f9303d9db6410f28951e42`
SHA-256: `b2a948a5340a9341fa3bab99507c7090c5b0456de72ec28b5b157b8df3f1d640`
来源:[`packages/core/agent/src/types.ts:110`](../packages/core/agent/src/types.ts)
| 属性 | 存在性 | 类型 |
|---|---|---|
| `inserted` | 必需 | [`UserMessage[]`](#persistence-type-sha256-454a0fa879e38076bdcdbd87ef561cd67d7e43e58a287cf7dbaa7b6cf7847020) |
| `inserted` | 必需 | [`UserMessage[]`](#persistence-type-sha256-5d00abf938272f22eb47199e9d40801e8d8ec4c88e2b138b15aa2a5a5bf87520) |
| `outcome` | 可选 | `"canceled"` |
| `removedCount` | 可选 | `number` |
| `start` | 必需 | `number` |
@@ -6419,6 +6434,91 @@ SHA-256: `0a30c3be336cdd4f8f34e66543a3d46d611c43986edd9b3ce6cbb9b721d3b5d3`
| `kind` | 必需 | `"aborted"` |
| `reason` | 必需 | [`TurnEndCancelCause`](#persistence-type-sha256-1f8c126cfab3df812cd115cfc23e3256f0fa2383400f4ce91ecad313577e59b0) |
<a id="persistence-type-sha256-dde0cdf9d426768b649b5a9783d2144e7f900c35ef65229cd89036dbf411d1b0"></a>
### `{ kind: "action-cards" }`
SHA-256: `dde0cdf9d426768b649b5a9783d2144e7f900c35ef65229cd89036dbf411d1b0`
| 属性 | 存在性 | 类型 |
|---|---|---|
| `kind` | 必需 | `"action-cards"` |
| `through` | 必需 | `number` |
<a id="persistence-type-sha256-dd00cdccd0ba487b92362ef67db58cd264f48c1bfd368291424a1da9a899489a"></a>
### `{ kind: "action-cards", form: "catalog" }`
SHA-256: `dd00cdccd0ba487b92362ef67db58cd264f48c1bfd368291424a1da9a899489a`
| 属性 | 存在性 | 类型 |
|---|---|---|
| `form` | 必需 | `"catalog"` |
| `kind` | 必需 | `"action-cards"` |
| `through` | 必需 | `number` |
<a id="persistence-type-sha256-ba2a1a8f194b1bb21f270713398f6643e206e8ff5f7f26f6d36a88e3f0f0ab10"></a>
### `{ kind: "action-cards", form: "instructions" }`
SHA-256: `ba2a1a8f194b1bb21f270713398f6643e206e8ff5f7f26f6d36a88e3f0f0ab10`
| 属性 | 存在性 | 类型 |
|---|---|---|
| `form` | 必需 | `"instructions"` |
| `kind` | 必需 | `"action-cards"` |
| `through` | 必需 | `number` |
<a id="persistence-type-sha256-e7f4cbb3a57f4cbdb61ea41e95bcba626ad1fff6bb9b1e063b1d50a32da6e864"></a>
### `{ kind: "action-cards", form: "notice" }`
SHA-256: `e7f4cbb3a57f4cbdb61ea41e95bcba626ad1fff6bb9b1e063b1d50a32da6e864`
| 属性 | 存在性 | 类型 |
|---|---|---|
| `form` | 必需 | `"notice"` |
| `kind` | 必需 | `"action-cards"` |
| `summary` | 必需 | `string` |
| `through` | 必需 | `number` |
<a id="persistence-type-sha256-b613f83572fe2fbf7259c72308cafd008f4fceea897749128a48bcd158f290aa"></a>
### `{ kind: "action-cards", form: "recall" }`
SHA-256: `b613f83572fe2fbf7259c72308cafd008f4fceea897749128a48bcd158f290aa`
| 属性 | 存在性 | 类型 |
|---|---|---|
| `form` | 必需 | `"recall"` |
| `kind` | 必需 | `"action-cards"` |
| `through` | 必需 | `number` |
<a id="persistence-type-sha256-45f005e6bbb7645248be792b715a74bc7fdf5a67f5381655d3a75dcc7aa2112e"></a>
### `{ kind: "action-cards", form: "relay" }`
SHA-256: `45f005e6bbb7645248be792b715a74bc7fdf5a67f5381655d3a75dcc7aa2112e`
| 属性 | 存在性 | 类型 |
|---|---|---|
| `form` | 必需 | `"relay"` |
| `kind` | 必需 | `"action-cards"` |
| `through` | 必需 | `number` |
<a id="persistence-type-sha256-3247e566f4340029325b70e4366b56fa9719977f1e39033b769a2ec221ab22a9"></a>
### `{ kind: "action-cards", form: "snapshot" }`
SHA-256: `3247e566f4340029325b70e4366b56fa9719977f1e39033b769a2ec221ab22a9`
| 属性 | 存在性 | 类型 |
|---|---|---|
| `form` | 必需 | `"snapshot"` |
| `kind` | 必需 | `"action-cards"` |
| `sections` | 必需 | [`ContextSnapshotSection[]`](#persistence-type-sha256-c748fb42c80a120038a8c587af80ec39924803c401364db11c8974d5bf6d0262) |
| `through` | 必需 | `number` |
<a id="persistence-type-sha256-023a355b2be0d688fb6e4df8388dadb942761d46cfe96d0f499aae9631a159d4"></a>
### `{ kind: "blocked" }`
@@ -7945,17 +8045,17 @@ SHA-256: `a10c17474eaf2ddab7095a099e0fe3d046fc18e56c3e344fc8894c05ff9ef97b`
| `time` | 必需 | `number` |
| `type` | 必需 | `"agent-preset/selected"` |
<a id="persistence-type-sha256-0755b7d1128319ba2e389619cbbf2c961ad648882b4e91da7f3e9582cc8a2034"></a>
<a id="persistence-type-sha256-b36fd842cd004b532ce96fd5d6f5be28e017eda043131aa3cf5e69edc436108e"></a>
<a id="persistence-type-eventagentinboxspliced"></a>
### `{ type: "agent/inbox/spliced" }`
SHA-256: `0755b7d1128319ba2e389619cbbf2c961ad648882b4e91da7f3e9582cc8a2034`
SHA-256: `b36fd842cd004b532ce96fd5d6f5be28e017eda043131aa3cf5e69edc436108e`
| 属性 | 存在性 | 类型 |
|---|---|---|
| `data` | 必需 | [`{ inserted, outcome?, removedCount?, start, … }`](#persistence-type-sha256-fa106c5b33b657e8ef16e8cbf39f3b7d44e5cbcdf4f9303d9db6410f28951e42) |
| `data` | 必需 | [`{ inserted, outcome?, removedCount?, start, … }`](#persistence-type-sha256-b2a948a5340a9341fa3bab99507c7090c5b0456de72ec28b5b157b8df3f1d640) |
| `ignorable` | 可选 | `true` |
| `seq` | 必需 | `number` |
| `time` | 必需 | `number` |
@@ -8196,17 +8296,17 @@ SHA-256: `13d3d180f977bf78081d487ffa0ecb75857349bcab29a5a3fb48189fca2a6176`
| `time` | 必需 | `number` |
| `type` | 必需 | `"deliverables/presented"` |
<a id="persistence-type-sha256-97e0162f983c27456ec71269fb4976b1851588b8c1aeac5d071d43e11f95e03c"></a>
<a id="persistence-type-sha256-5ef58f2a1bdc576c1f12fa1f4da1920ece020b13c0f991a578cddcbeb28bc5be"></a>
<a id="persistence-type-eventdevelopermessage"></a>
### `{ type: "developer/message" }`
SHA-256: `97e0162f983c27456ec71269fb4976b1851588b8c1aeac5d071d43e11f95e03c`
SHA-256: `5ef58f2a1bdc576c1f12fa1f4da1920ece020b13c0f991a578cddcbeb28bc5be`
| 属性 | 存在性 | 类型 |
|---|---|---|
| `data` | 必需 | [`{ headerSeq?, message, step, turn }`](#persistence-type-sha256-f4ed4717b4c259e933b550a8dcd90dc93628cda8a3c7affcaa53b9c13c1d53eb) |
| `data` | 必需 | [`{ headerSeq?, message, step, turn }`](#persistence-type-sha256-7404c6123fdaa591474a37979a5a8ae70d1387af7c9f09bfe568b218935a974d) |
| `ignorable` | 可选 | `true` |
| `seq` | 必需 | `number` |
| `sourceEventSeqs` | 可选 | [`number[]`](#persistence-type-sha256-5d03ba38734809bcbd2a55221bd938b3b3fc34b49bb686c6113e9e4931e3aa78) |
@@ -8606,17 +8706,17 @@ SHA-256: `1b912703e2d64f91c99c675b8f805b01076c8325b905c1218ad81ef0b24909d5`
| `time` | 必需 | `number` |
| `type` | 必需 | `"session/title"` |
<a id="persistence-type-sha256-79a4c87f8bfb4d6d05cd9952b99210d6a7db3b478c4a01d94b4c50602308c056"></a>
<a id="persistence-type-sha256-efa1a091017ce533e38b0ef3e14d11a5c102ff1793229b18dad719a4268d9adc"></a>
<a id="persistence-type-eventsessiontitle-llm-request"></a>
### `{ type: "session/title-llm-request" }`
SHA-256: `79a4c87f8bfb4d6d05cd9952b99210d6a7db3b478c4a01d94b4c50602308c056`
SHA-256: `efa1a091017ce533e38b0ef3e14d11a5c102ff1793229b18dad719a4268d9adc`
| 属性 | 存在性 | 类型 |
|---|---|---|
| `data` | 必需 | [`SessionTitleLlmRequestEventData`](#persistence-type-sha256-00c054d3018c198d92f459563331edd1385e58345c677bb499791df2da0ade9f) |
| `data` | 必需 | [`SessionTitleLlmRequestEventData`](#persistence-type-sha256-a3e299483348f27a4787a36bb3f29cd86024c65336abc10bb3d687468302c4cf) |
| `ignorable` | 可选 | `true` |
| `seq` | 必需 | `number` |
| `time` | 必需 | `number` |
@@ -9039,17 +9139,17 @@ SHA-256: `f1a96afe7503ac1817de769cd0ba13d9b80111c7034c50106b3344ecc14cc515`
| `type` | 必需 | `"usage"` |
| `usage` | 必需 | [`TokenUsage`](#persistence-type-sha256-fec5442b9ee4122e10514d81d5a43648003fe8bfb7ee2ba50c0cdbdd383ed394) |
<a id="persistence-type-sha256-2ae8594a998c9862a597129e782277bcc518266712a703b5c3161bdf552d13b8"></a>
<a id="persistence-type-sha256-58b122df513aeaf7ddc5783dbb1fd844e2979bb273964e30ae351a8db6510019"></a>
<a id="persistence-type-eventusermessage"></a>
### `{ type: "user/message" }`
SHA-256: `2ae8594a998c9862a597129e782277bcc518266712a703b5c3161bdf552d13b8`
SHA-256: `58b122df513aeaf7ddc5783dbb1fd844e2979bb273964e30ae351a8db6510019`
| 属性 | 存在性 | 类型 |
|---|---|---|
| `data` | 必需 | [`UserMessage`](#persistence-type-sha256-2cf075b013d363ad743b5a4982900678d108499c721bc11255137402003db711) |
| `data` | 必需 | [`UserMessage`](#persistence-type-sha256-b782058cef83caf3eb44a16adeef5657e4d5cdff2d50655aba418c4490670a69) |
| `ignorable` | 可选 | `true` |
| `seq` | 必需 | `number` |
| `sourceEventSeqs` | 可选 | [`number[]`](#persistence-type-sha256-5d03ba38734809bcbd2a55221bd938b3b3fc34b49bb686c6113e9e4931e3aa78) |
@@ -0,0 +1,28 @@
# Bilingual-pair consistency record for 2026-10-22-action-card-status.md (docs/i18n/README.md): per heading
# section, a hash of its English and Chinese blocks outside code blocks and generated regions.
# After editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/persistence-changes/2026-10-22-action-card-status.md
/:
en: 3d5a424ae7d7e46b
zh: c22a8e4a62d8cc1a
/2026-10-22-action-card-status:
en: f580a2fb64e941db
zh: 0e34217155210da6
/2026-10-22-action-card-status/summary:
en: 3da2594bf2891e0e
zh: 5bc7d279d99400aa
/2026-10-22-action-card-status/table-of-contents:
en: 6cf314a069ceb451
zh: 9adff678e9ecab3b
/2026-10-22-action-card-status/declaration:
en: 6907136f28e48c90
zh: 02f8298485f3d8e7
/2026-10-22-action-card-status/compatibility:
en: fbf4d1ada55f35fe
zh: bd5ce9a23c55fac6
/2026-10-22-action-card-status/verification:
en: 91043c4f0b19e7bc
zh: ee1c541133475bb8
/2026-10-22-action-card-status/dev-note:
en: b75c8458928b72b9
zh: 003ba4e17b4b5269
@@ -0,0 +1,60 @@
---
description: "Records a persistence type transition and its compatibility acknowledgement."
kind: persistence-change
---
# 2026-10-22-action-card-status
English | [中文](2026-10-22-action-card-status.zh.md)
## Summary
Adds action-card status attribution to logged model context.
## Table of Contents
- [Declaration](#declaration)
- [Compatibility](#compatibility)
- [Verification](#verification)
- [Dev Note](#dev-note)
<a id="declaration"></a>
## Declaration
```yaml persistence-change
schemaVersion: 1
id: 2026-10-22-action-card-status
baseline: false
changes:
- root: "event:agent/inbox/spliced"
previous: "2026-10-13-user-rpc-principal"
after: "b36fd842cd004b532ce96fd5d6f5be28e017eda043131aa3cf5e69edc436108e"
decision: same-version
- root: "event:developer/message"
previous: "2026-10-13-user-rpc-principal"
after: "5ef58f2a1bdc576c1f12fa1f4da1920ece020b13c0f991a578cddcbeb28bc5be"
decision: same-version
- root: "event:session/title-llm-request"
previous: "2026-10-13-user-rpc-principal"
after: "efa1a091017ce533e38b0ef3e14d11a5c102ff1793229b18dad719a4268d9adc"
decision: same-version
- root: "event:user/message"
previous: "2026-10-13-user-rpc-principal"
after: "58b122df513aeaf7ddc5783dbb1fd844e2979bb273964e30ae351a8db6510019"
decision: same-version
```
<a id="compatibility"></a>
## Compatibility
Existing messages and records are unchanged. Readers without the action-card plugin preserve the new action-cards source kind and its JSON metadata and derive the recorded text as ordinary context. Only the producer uses the through sequence, to acknowledge delivered decisions; it grants no authority and does not change replay interpretation.
<a id="verification"></a>
## Verification
pnpm exec vitest run on the action-cards, creation-recommendations, client-ui-action-cards and client-ui-creation-recommendations tests: 120 tests passed with 100% coverage of their sources. The Web creation-recommendations replay logs the status notice of a dismissed card before the next answer.
<a id="dev-note"></a>
## Dev Note
None.
@@ -0,0 +1,249 @@
{
"formatVersion": 2,
"roots": [
{"key":"event:agent/inbox/spliced","kind":"event","event":"agent/inbox/spliced","surface":false,"digest":"b36fd842cd004b532ce96fd5d6f5be28e017eda043131aa3cf5e69edc436108e"},
{"key":"event:developer/message","kind":"event","event":"developer/message","surface":true,"digest":"5ef58f2a1bdc576c1f12fa1f4da1920ece020b13c0f991a578cddcbeb28bc5be"},
{"key":"event:session/title-llm-request","kind":"event","event":"session/title-llm-request","surface":false,"digest":"efa1a091017ce533e38b0ef3e14d11a5c102ff1793229b18dad719a4268d9adc"},
{"key":"event:user/message","kind":"event","event":"user/message","surface":true,"digest":"58b122df513aeaf7ddc5783dbb1fd844e2979bb273964e30ae351a8db6510019"}
],
"types": [],
"nodes": {
"0203243b72fc867a": {"kind":"object","properties":[{"name":"kind","type":"24b10f7cdae64928","optional":false}],"indices":[]},
"022623b9ef98ec47": {"kind":"object","properties":[{"name":"form","type":"383cf73ae4748e5b","optional":false},{"name":"kind","type":"f68c335306535a00","optional":false}],"indices":[]},
"02b2c23f2d94b028": {"kind":"object","properties":[{"name":"kind","type":"c5687b1313206a6f","optional":false}],"indices":[]},
"05205074b24d8340": {"kind":"literal","value":"action-cards"},
"0657d3ca5ac4e7bf": {"kind":"primitive","type":"number"},
"07e1a58c58b593f5": {"kind":"object","properties":[{"name":"model","type":"4cd48f3e5108bd6e","optional":false},{"name":"provider","type":"4cd48f3e5108bd6e","optional":false}],"indices":[]},
"09655b34d7f3cf1e": {"kind":"literal","value":"system-prompt"},
"09f3f8dc417f2782": {"kind":"object","properties":[{"name":"content","type":"b52f215a6cd69105","optional":false},{"name":"id","type":"4cd48f3e5108bd6e","optional":false},{"name":"role","type":"8af841fc9b265ade","optional":false},{"name":"source","type":"65a8400e451e667e","optional":false}],"indices":[]},
"0ed8d1108fe7a5e1": {"kind":"object","properties":[{"name":"kind","type":"a9059a81430b3207","optional":false}],"indices":[]},
"0eeb17e1df89b1eb": {"kind":"object","properties":[{"name":"form","type":"fee1caf8ab6a65c1","optional":false},{"name":"kind","type":"24b10f7cdae64928","optional":false}],"indices":[]},
"0f46153645c29784": {"kind":"object","properties":[{"name":"height","type":"0657d3ca5ac4e7bf","optional":false},{"name":"width","type":"0657d3ca5ac4e7bf","optional":false}],"indices":[]},
"0fe52387f6505270": {"kind":"literal","value":"plan-mode"},
"11638dc58a31496f": {"kind":"object","properties":[{"name":"form","type":"6f9e00383836275e","optional":false},{"name":"kind","type":"2f3f201c8846e19c","optional":false},{"name":"sections","type":"c748fb42c80a1200","optional":false}],"indices":[]},
"12d5808fac1996b9": {"kind":"object","properties":[{"name":"attachment","type":"b0b0eb1b16490f6b","optional":false},{"name":"offloaded","type":"dacf3d5f3e4ff619","optional":true},{"name":"type","type":"fb8b6a2e53cb9cf4","optional":false}],"indices":[]},
"1379f0b72275994d": {"kind":"literal","value":"developer/message"},
"16d0507cb94b4cfc": {"kind":"literal","value":"relay"},
"1922cd79a70806c1": {"kind":"literal","value":"skill-invocation"},
"1938c8c20f1cba7c": {"kind":"object","properties":[{"name":"form","type":"fee1caf8ab6a65c1","optional":false},{"name":"kind","type":"d9edf1862e4471d6","optional":false}],"indices":[]},
"1e709346005826eb": {"kind":"object","properties":[{"name":"form","type":"aec150797b655c5f","optional":false},{"name":"kind","type":"d9edf1862e4471d6","optional":false}],"indices":[]},
"1f0133217eeeffd8": {"kind":"literal","value":"tool-call"},
"1f1385dbc3e875c0": {"kind":"literal","value":"repeat-tool-reminder"},
"1fdad624334d3548": {"kind":"object","properties":[{"name":"form","type":"16d0507cb94b4cfc","optional":false},{"name":"kind","type":"bb94f3217aa56c88","optional":false}],"indices":[]},
"2152caf1a71e588e": {"kind":"object","properties":[{"name":"kind","type":"2e4debca12f2c449","optional":false},{"name":"messageId","type":"4cd48f3e5108bd6e","optional":false},{"name":"senderId","type":"4cd48f3e5108bd6e","optional":false},{"name":"senderName","type":"4cd48f3e5108bd6e","optional":false},{"name":"teamId","type":"4cd48f3e5108bd6e","optional":false}],"indices":[]},
"2235d957bf4b7c6c": {"kind":"object","properties":[{"name":"kind","type":"7b1ce60193fa8b69","optional":false}],"indices":[]},
"224aa11d4d5f88c4": {"kind":"object","properties":[{"name":"form","type":"fee1caf8ab6a65c1","optional":false},{"name":"kind","type":"23af3759678a30de","optional":false}],"indices":[]},
"23af3759678a30de": {"kind":"literal","value":"dsh-session-title-llm"},
"24b10f7cdae64928": {"kind":"literal","value":"runtime-context"},
"24ce31aa528272e5": {"kind":"object","properties":[{"name":"kind","type":"99e7e7d7fed55ae6","optional":false}],"indices":[]},
"260523b412c19ad8": {"kind":"object","properties":[{"name":"form","type":"aec150797b655c5f","optional":false},{"name":"kind","type":"2f3f201c8846e19c","optional":false}],"indices":[]},
"2765e9119edf5816": {"kind":"literal","value":"model"},
"277b61f9c6f81704": {"kind":"literal","value":"file"},
"28936c614bd4e7e2": {"kind":"object","properties":[{"name":"form","type":"6f9e00383836275e","optional":false},{"name":"kind","type":"d9edf1862e4471d6","optional":false},{"name":"sections","type":"c748fb42c80a1200","optional":false}],"indices":[]},
"290f32bfd81ff85a": {"kind":"object","properties":[{"name":"kind","type":"23af3759678a30de","optional":false}],"indices":[]},
"297fbd8067d7815a": {"kind":"object","properties":[{"name":"form","type":"aec150797b655c5f","optional":false},{"name":"kind","type":"a9059a81430b3207","optional":false}],"indices":[]},
"2aad1179f9576314": {"kind":"object","properties":[{"name":"form","type":"6f9e00383836275e","optional":false},{"name":"kind","type":"24b10f7cdae64928","optional":false},{"name":"sections","type":"c748fb42c80a1200","optional":false}],"indices":[]},
"2ab12a0e345658f9": {"kind":"array","element":"83da72c6857601c7"},
"2e4debca12f2c449": {"kind":"literal","value":"team-message"},
"2f1e6a7ebace43d7": {"kind":"union","types":["3f74ba74acd10644","58f94390b982f8a5","de30b1fa12c75f81","ebb768d85ec87fb6"]},
"2f3f201c8846e19c": {"kind":"literal","value":"tool-goal"},
"312aee2f0c42aabb": {"kind":"object","properties":[{"name":"form","type":"e8de0f457fed5b9f","optional":false},{"name":"kind","type":"d9edf1862e4471d6","optional":false},{"name":"summary","type":"4cd48f3e5108bd6e","optional":false}],"indices":[]},
"31907de956375182": {"kind":"object","properties":[{"name":"compactionId","type":"4cd48f3e5108bd6e","optional":false},{"name":"kind","type":"6b23ed9ce1b397e6","optional":false},{"name":"sourceCommandId","type":"4cd48f3e5108bd6e","optional":true}],"indices":[]},
"3247e566f4340029": {"kind":"object","properties":[{"name":"form","type":"6f9e00383836275e","optional":false},{"name":"kind","type":"05205074b24d8340","optional":false},{"name":"sections","type":"c748fb42c80a1200","optional":false},{"name":"through","type":"0657d3ca5ac4e7bf","optional":false}],"indices":[]},
"335e242de1fcc17b": {"kind":"union","types":["bcf0caf62d964b2f","e438a9d4fb883b3e"]},
"383cf73ae4748e5b": {"kind":"literal","value":"recall"},
"3b04790e08b35241": {"kind":"object","properties":[{"name":"form","type":"aec150797b655c5f","optional":false},{"name":"kind","type":"0fe52387f6505270","optional":false}],"indices":[]},
"3b4f2c9ef2c1217e": {"kind":"object","properties":[{"name":"form","type":"383cf73ae4748e5b","optional":false},{"name":"kind","type":"bb94f3217aa56c88","optional":false}],"indices":[]},
"3bd652ebfa8726b3": {"kind":"union","types":["0657d3ca5ac4e7bf","938f7c7357bc403e"]},
"3bdb8dc6989655b0": {"kind":"object","properties":[{"name":"form","type":"6f9e00383836275e","optional":false},{"name":"kind","type":"a9059a81430b3207","optional":false},{"name":"sections","type":"c748fb42c80a1200","optional":false}],"indices":[]},
"3c2535ca0a2062f1": {"kind":"literal","value":"reasoning"},
"3e2ece4967cfebe9": {"kind":"literal","value":"next-step"},
"3f74ba74acd10644": {"kind":"literal","value":"image/jpeg"},
"408927267422a7b2": {"kind":"object","properties":[{"name":"form","type":"fee1caf8ab6a65c1","optional":false},{"name":"kind","type":"0fe52387f6505270","optional":false}],"indices":[]},
"43e311235aa537df": {"kind":"object","properties":[{"name":"toolName","type":"4cd48f3e5108bd6e","optional":false},{"name":"type","type":"83d6916021eede8f","optional":false}],"indices":[]},
"45f005e6bbb76452": {"kind":"object","properties":[{"name":"form","type":"16d0507cb94b4cfc","optional":false},{"name":"kind","type":"05205074b24d8340","optional":false},{"name":"through","type":"0657d3ca5ac4e7bf","optional":false}],"indices":[]},
"4991dfc415c9efeb": {"kind":"object","properties":[{"name":"form","type":"16d0507cb94b4cfc","optional":false},{"name":"kind","type":"1f1385dbc3e875c0","optional":false}],"indices":[]},
"49dab019d00e8c26": {"kind":"object","properties":[{"name":"form","type":"6f9e00383836275e","optional":false},{"name":"kind","type":"78a9be340090b735","optional":false},{"name":"sections","type":"c748fb42c80a1200","optional":false}],"indices":[]},
"4a9bef9f27a94252": {"kind":"object","properties":[{"name":"form","type":"16d0507cb94b4cfc","optional":false},{"name":"kind","type":"82470020b5bfcb1a","optional":false},{"name":"senderSessionId","type":"4cd48f3e5108bd6e","optional":false}],"indices":[]},
"4b259f7f8f0a44ad": {"kind":"object","properties":[{"name":"content","type":"b52f215a6cd69105","optional":false},{"name":"id","type":"4cd48f3e5108bd6e","optional":false},{"name":"isError","type":"aa93010d0e521ad7","optional":true},{"name":"role","type":"a13d8f379222ece8","optional":false},{"name":"source","type":"d9c462b9f62fdd3e","optional":false},{"name":"toolCallId","type":"4cd48f3e5108bd6e","optional":false}],"indices":[]},
"4cd48f3e5108bd6e": {"kind":"primitive","type":"string"},
"4e44c9118152f682": {"kind":"literal","value":"text"},
"4e88776858652856": {"kind":"object","properties":[{"name":"text","type":"4cd48f3e5108bd6e","optional":false},{"name":"type","type":"4e44c9118152f682","optional":false}],"indices":[]},
"4e97c3d85c0fc817": {"kind":"object","properties":[{"name":"form","type":"e8de0f457fed5b9f","optional":false},{"name":"kind","type":"bf16d2e98a7f1389","optional":false},{"name":"senderSessionId","type":"4cd48f3e5108bd6e","optional":false},{"name":"summary","type":"4cd48f3e5108bd6e","optional":false}],"indices":[]},
"4edec1b770e8ec4c": {"kind":"literal","value":"ptc-mode"},
"4f91f187baeabef5": {"kind":"object","properties":[{"name":"capturedFormatVersion","type":"0657d3ca5ac4e7bf","optional":true},{"name":"capturedThroughSeq","type":"3bd652ebfa8726b3","optional":false},{"name":"compacted","type":"aa93010d0e521ad7","optional":false},{"name":"inputIndex","type":"0657d3ca5ac4e7bf","optional":false},{"name":"label","type":"4cd48f3e5108bd6e","optional":false},{"name":"omittedBytes","type":"0657d3ca5ac4e7bf","optional":false},{"name":"omittedMessages","type":"0657d3ca5ac4e7bf","optional":false},{"name":"originalMessages","type":"0657d3ca5ac4e7bf","optional":false},{"name":"retainedMessages","type":"0657d3ca5ac4e7bf","optional":false},{"name":"sessionId","type":"4cd48f3e5108bd6e","optional":false},{"name":"truncated","type":"aa93010d0e521ad7","optional":false}],"indices":[]},
"52c6113cb0e53f34": {"kind":"object","properties":[{"name":"form","type":"6f9e00383836275e","optional":false},{"name":"kind","type":"9ac52f0f84fec2eb","optional":false},{"name":"sections","type":"c748fb42c80a1200","optional":false}],"indices":[]},
"53cf1704ae0709c7": {"kind":"object","properties":[{"name":"form","type":"fee1caf8ab6a65c1","optional":false},{"name":"kind","type":"99e7e7d7fed55ae6","optional":false}],"indices":[]},
"5654685d7557ec74": {"kind":"object","properties":[{"name":"form","type":"6f9e00383836275e","optional":false},{"name":"kind","type":"f68c335306535a00","optional":false},{"name":"sections","type":"c748fb42c80a1200","optional":false}],"indices":[]},
"58015da1c1950a66": {"kind":"object","properties":[{"name":"form","type":"aec150797b655c5f","optional":false},{"name":"kind","type":"24b10f7cdae64928","optional":false}],"indices":[]},
"587bb11da730e06a": {"kind":"literal","value":"session/title-llm-request"},
"589d0d91239681f8": {"kind":"literal","value":"developer"},
"58b122df513aeaf7": {"kind":"object","properties":[{"name":"data","type":"b782058cef83caf3","optional":false},{"name":"ignorable","type":"dacf3d5f3e4ff619","optional":true},{"name":"seq","type":"0657d3ca5ac4e7bf","optional":false},{"name":"sourceEventSeqs","type":"5d03ba38734809bc","optional":true},{"name":"surfaceOp","type":"335e242de1fcc17b","optional":false},{"name":"time","type":"0657d3ca5ac4e7bf","optional":false},{"name":"type","type":"de25352d2dc41963","optional":false}],"indices":[]},
"58f94390b982f8a5": {"kind":"literal","value":"image/png"},
"5bb69b88517f6214": {"kind":"object","properties":[{"name":"form","type":"e8de0f457fed5b9f","optional":false},{"name":"kind","type":"99e7e7d7fed55ae6","optional":false},{"name":"summary","type":"4cd48f3e5108bd6e","optional":false}],"indices":[]},
"5bc66269cfaf614c": {"kind":"object","properties":[{"name":"form","type":"16d0507cb94b4cfc","optional":false},{"name":"kind","type":"d9edf1862e4471d6","optional":false}],"indices":[]},
"5cdbf342b7748d15": {"kind":"object","properties":[{"name":"form","type":"16d0507cb94b4cfc","optional":false},{"name":"kind","type":"f68c335306535a00","optional":false}],"indices":[]},
"5d00abf938272f22": {"kind":"array","element":"b782058cef83caf3"},
"5d03ba38734809bc": {"kind":"array","element":"0657d3ca5ac4e7bf"},
"5d7a64ed6aac5551": {"kind":"object","properties":[{"name":"form","type":"16d0507cb94b4cfc","optional":false},{"name":"kind","type":"8067b0d232c350f1","optional":false}],"indices":[]},
"5ef58f2a1bdc576c": {"kind":"object","properties":[{"name":"data","type":"7404c6123fdaa591","optional":false},{"name":"ignorable","type":"dacf3d5f3e4ff619","optional":true},{"name":"seq","type":"0657d3ca5ac4e7bf","optional":false},{"name":"sourceEventSeqs","type":"5d03ba38734809bc","optional":true},{"name":"surfaceOp","type":"335e242de1fcc17b","optional":false},{"name":"time","type":"0657d3ca5ac4e7bf","optional":false},{"name":"type","type":"1379f0b72275994d","optional":false}],"indices":[]},
"6085488d223aeffd": {"kind":"object","properties":[{"name":"form","type":"fee1caf8ab6a65c1","optional":false},{"name":"kind","type":"2f3f201c8846e19c","optional":false}],"indices":[]},
"6292bcbedaa97fea": {"kind":"object","properties":[{"name":"kind","type":"f68c335306535a00","optional":false}],"indices":[]},
"62ecb62dc0fe1b5e": {"kind":"literal","value":"next-turn"},
"65a8400e451e667e": {"kind":"object","properties":[{"name":"kind","type":"09655b34d7f3cf1e","optional":false}],"indices":[]},
"672c4aa2a9b736e3": {"kind":"object","properties":[{"name":"form","type":"fee1caf8ab6a65c1","optional":false},{"name":"kind","type":"1922cd79a70806c1","optional":false},{"name":"name","type":"4cd48f3e5108bd6e","optional":false}],"indices":[]},
"68f44f59e91c4139": {"kind":"literal","value":"user"},
"6b23ed9ce1b397e6": {"kind":"literal","value":"compact-checkpoint"},
"6ccb6bc423fb3952": {"kind":"object","properties":[{"name":"form","type":"e8de0f457fed5b9f","optional":false},{"name":"kind","type":"f68c335306535a00","optional":false},{"name":"summary","type":"4cd48f3e5108bd6e","optional":false}],"indices":[]},
"6dacb1584f2bfbe2": {"kind":"object","properties":[{"name":"clientTimeZone","type":"4cd48f3e5108bd6e","optional":true},{"name":"kind","type":"68f44f59e91c4139","optional":false},{"name":"principal","type":"deebfc7c62177ea8","optional":true},{"name":"rpcId","type":"4cd48f3e5108bd6e","optional":false}],"indices":[]},
"6ea608bdc8a7a63d": {"kind":"object","properties":[{"name":"form","type":"16d0507cb94b4cfc","optional":false},{"name":"kind","type":"a9059a81430b3207","optional":false}],"indices":[]},
"6ebcc2dbec9f2dab": {"kind":"literal","value":"skill-catalog"},
"6f22a9d02b9a1f11": {"kind":"object","properties":[{"name":"baseline","type":"dacf3d5f3e4ff619","optional":true},{"name":"baselineIdentity","type":"4cd48f3e5108bd6e","optional":true},{"name":"changes","type":"2ab12a0e345658f9","optional":false},{"name":"form","type":"fee1caf8ab6a65c1","optional":false},{"name":"kind","type":"fbd2c74b346a1595","optional":false}],"indices":[]},
"6f44f32be9bcd9a8": {"kind":"object","properties":[{"name":"form","type":"fee1caf8ab6a65c1","optional":false},{"name":"kind","type":"1f1385dbc3e875c0","optional":false}],"indices":[]},
"6f9e00383836275e": {"kind":"literal","value":"snapshot"},
"719c51f67074f970": {"kind":"object","properties":[{"name":"kind","type":"2765e9119edf5816","optional":false},{"name":"model","type":"4cd48f3e5108bd6e","optional":false},{"name":"provider","type":"4cd48f3e5108bd6e","optional":false},{"name":"replayState","type":"bbc5696e3f13c758","optional":true}],"indices":[]},
"7382c4fc95fe8c5d": {"kind":"literal","value":"remove"},
"7404c6123fdaa591": {"kind":"object","properties":[{"name":"headerSeq","type":"0657d3ca5ac4e7bf","optional":true},{"name":"message","type":"7580e7d96dc8475f","optional":false},{"name":"step","type":"0657d3ca5ac4e7bf","optional":false},{"name":"turn","type":"0657d3ca5ac4e7bf","optional":false}],"indices":[]},
"7554565be416b034": {"kind":"object","properties":[{"name":"form","type":"383cf73ae4748e5b","optional":false},{"name":"kind","type":"2f3f201c8846e19c","optional":false}],"indices":[]},
"756b5e23a5cb2c7f": {"kind":"object","properties":[{"name":"form","type":"383cf73ae4748e5b","optional":false},{"name":"kind","type":"8067b0d232c350f1","optional":false}],"indices":[]},
"7580e7d96dc8475f": {"kind":"object","properties":[{"name":"content","type":"b52f215a6cd69105","optional":false},{"name":"id","type":"4cd48f3e5108bd6e","optional":false},{"name":"role","type":"589d0d91239681f8","optional":false},{"name":"source","type":"7a5b68bd1a5118c8","optional":false,"compatibility":{"version":1,"policy":"session-source-attribution","binding":"session.developer-message.source","discriminator":"kind","unknownKinds":"preserve","attributionKinds":["action-cards","tmux-context","user-question-reply","working-directory"]}}],"indices":[]},
"78a9be340090b735": {"kind":"literal","value":"tmux-context"},
"78b9204976ab9faa": {"kind":"array","element":"cda79b839d8cc0c7"},
"7a5b68bd1a5118c8": {"kind":"union","types":["0203243b72fc867a","022623b9ef98ec47","02b2c23f2d94b028","0ed8d1108fe7a5e1","0eeb17e1df89b1eb","11638dc58a31496f","1938c8c20f1cba7c","1e709346005826eb","1fdad624334d3548","2152caf1a71e588e","2235d957bf4b7c6c","224aa11d4d5f88c4","24ce31aa528272e5","260523b412c19ad8","28936c614bd4e7e2","290f32bfd81ff85a","297fbd8067d7815a","2aad1179f9576314","312aee2f0c42aabb","31907de956375182","3247e566f4340029","3b04790e08b35241","3b4f2c9ef2c1217e","3bdb8dc6989655b0","408927267422a7b2","45f005e6bbb76452","4991dfc415c9efeb","49dab019d00e8c26","4a9bef9f27a94252","4e97c3d85c0fc817","52c6113cb0e53f34","53cf1704ae0709c7","5654685d7557ec74","58015da1c1950a66","5bb69b88517f6214","5bc66269cfaf614c","5cdbf342b7748d15","5d7a64ed6aac5551","6085488d223aeffd","6292bcbedaa97fea","65a8400e451e667e","672c4aa2a9b736e3","6ccb6bc423fb3952","6dacb1584f2bfbe2","6ea608bdc8a7a63d","6f22a9d02b9a1f11","6f44f32be9bcd9a8","719c51f67074f970","7554565be416b034","756b5e23a5cb2c7f","7df62978a2b7de85","7eaef7a763ba5b94","7f959241c24128b4","80677c8c8b804189","825ca23416b1ea9d","8338ee303e2bd448","854db3efdfb0cd39","85fe96bf15d5c755","8620189c8e335827","87b1a33564b9a4d6","88841202942c7683","8a046b52331959aa","8c1c4f72755a78ea","8c627d7a45fa406c","8f324cea3916c051","8fcb0b44a575e998","96bfb916a727cd44","99135d8a39379779","99f0e98184709ad1","9edc162949abf93b","9f65c43e340bba31","a15a19ef63cf5d1b","a16e567c8cd2c55c","a2066bd52f61218b","a3c6f1ea38e9a61d","aa2bf9e82b9fdeb9","aae343bc3d663c83","ada310bf0bdb8fed","ade057f4fdc387ae","b062078e899958f9","b2a5c8c008a31e3d","b3681c026a0b98e9","b4ec0af39fd52ed9","b613f83572fe2fbf","b6f94c17dd9bbd7c","b70f3e81fe11b7a5","b79641a9d25e7566","ba2a1a8f194b1bb2","ba4c40b4381e0dcd","baccc97ec34be6c4","c5228b7a55498d84","c55827fba7018198","cb1c8959e1726f82","cda2345a9be9d139","d85590a5675c6e2f","d9c462b9f62fdd3e","db06ef35b27a3842","dba54c09c6e7aafc","dd00cdccd0ba487b","dd5ee8762bd8a14b","dd6cc2bb2a8f2824","dde0cdf9d426768b","deaae5cd30987d4f","e7f4cbb3a57f4cbd","edbbfff1128099fd","ee577a0cc18664c9","f230c0b583e23b25","f29d63336e8266d8","f4a66f654996c093","f56c5b59ca016131","f6329fb30c7ea801","f6605d6c57933aea","f6afe41e3926f422","f7283223aacdc39b","f97f47b0eb87905e","fc2ae6962f9f8801","fcf81d3c58c13593","fe1a7acdeed4d0f5"]},
"7b1ce60193fa8b69": {"kind":"literal","value":"working-directory"},
"7df62978a2b7de85": {"kind":"object","properties":[{"name":"form","type":"aec150797b655c5f","optional":false},{"name":"kind","type":"78a9be340090b735","optional":false}],"indices":[]},
"7eaef7a763ba5b94": {"kind":"object","properties":[{"name":"form","type":"16d0507cb94b4cfc","optional":false},{"name":"kind","type":"23af3759678a30de","optional":false}],"indices":[]},
"7f13860ee89a2d35": {"kind":"literal","value":"session-reference"},
"7f959241c24128b4": {"kind":"object","properties":[{"name":"goalId","type":"4cd48f3e5108bd6e","optional":false},{"name":"kind","type":"b815d7b91cdd9fc6","optional":false},{"name":"revision","type":"0657d3ca5ac4e7bf","optional":false},{"name":"round","type":"0657d3ca5ac4e7bf","optional":false}],"indices":[]},
"8064c03c87c357fd": {"kind":"union","types":["12d5808fac1996b9","43e311235aa537df","4e88776858652856","814d9434e1ddf907","8e611bf46821e500","c1f0f17b6493b85d","f2da0f27c1e6add5"]},
"80677c8c8b804189": {"kind":"object","properties":[{"name":"form","type":"aec150797b655c5f","optional":false},{"name":"kind","type":"23af3759678a30de","optional":false}],"indices":[]},
"8067b0d232c350f1": {"kind":"literal","value":"time-context"},
"814d9434e1ddf907": {"kind":"object","properties":[{"name":"text","type":"4cd48f3e5108bd6e","optional":false},{"name":"type","type":"3c2535ca0a2062f1","optional":false}],"indices":[]},
"82470020b5bfcb1a": {"kind":"literal","value":"agent-message"},
"825ca23416b1ea9d": {"kind":"object","properties":[{"name":"form","type":"aec150797b655c5f","optional":false},{"name":"kind","type":"f68c335306535a00","optional":false}],"indices":[]},
"829a2fb3be6efd4b": {"kind":"literal","value":"agent/inbox/spliced"},
"83079c8a3f733ac3": {"kind":"object","properties":[{"name":"description","type":"4cd48f3e5108bd6e","optional":false},{"name":"name","type":"4cd48f3e5108bd6e","optional":false}],"indices":[]},
"8338ee303e2bd448": {"kind":"object","properties":[{"name":"form","type":"e8de0f457fed5b9f","optional":false},{"name":"kind","type":"23af3759678a30de","optional":false},{"name":"summary","type":"4cd48f3e5108bd6e","optional":false}],"indices":[]},
"83d6916021eede8f": {"kind":"literal","value":"tool-removal"},
"83da72c6857601c7": {"kind":"object","properties":[{"name":"action","type":"904bf499d98be4a3","optional":false},{"name":"digest","type":"4cd48f3e5108bd6e","optional":true},{"name":"path","type":"4cd48f3e5108bd6e","optional":false},{"name":"scope","type":"4cd48f3e5108bd6e","optional":false}],"indices":[]},
"854db3efdfb0cd39": {"kind":"object","properties":[{"name":"form","type":"e8de0f457fed5b9f","optional":false},{"name":"kind","type":"bb94f3217aa56c88","optional":false},{"name":"summary","type":"4cd48f3e5108bd6e","optional":false}],"indices":[]},
"85d6d96ff8842974": {"kind":"literal","value":"answered"},
"85f9b505b7bdff1f": {"kind":"literal","value":"tool-addition"},
"85fe96bf15d5c755": {"kind":"object","properties":[{"name":"form","type":"aec150797b655c5f","optional":false},{"name":"kind","type":"8067b0d232c350f1","optional":false}],"indices":[]},
"8620189c8e335827": {"kind":"object","properties":[{"name":"form","type":"383cf73ae4748e5b","optional":false},{"name":"kind","type":"1f1385dbc3e875c0","optional":false}],"indices":[]},
"86e1e384495ee09a": {"kind":"object","properties":[{"name":"name","type":"4cd48f3e5108bd6e","optional":false},{"name":"text","type":"4cd48f3e5108bd6e","optional":false}],"indices":[]},
"87b1a33564b9a4d6": {"kind":"object","properties":[{"name":"form","type":"383cf73ae4748e5b","optional":false},{"name":"kind","type":"a9059a81430b3207","optional":false}],"indices":[]},
"88841202942c7683": {"kind":"object","properties":[{"name":"form","type":"6f9e00383836275e","optional":false},{"name":"kind","type":"bb94f3217aa56c88","optional":false},{"name":"sections","type":"c748fb42c80a1200","optional":false}],"indices":[]},
"8a046b52331959aa": {"kind":"object","properties":[{"name":"kind","type":"78a9be340090b735","optional":false}],"indices":[]},
"8af841fc9b265ade": {"kind":"literal","value":"system"},
"8c1c4f72755a78ea": {"kind":"object","properties":[{"name":"form","type":"6f9e00383836275e","optional":false},{"name":"kind","type":"0fe52387f6505270","optional":false},{"name":"sections","type":"c748fb42c80a1200","optional":false}],"indices":[]},
"8c627d7a45fa406c": {"kind":"object","properties":[{"name":"form","type":"e8de0f457fed5b9f","optional":false},{"name":"kind","type":"2f3f201c8846e19c","optional":false},{"name":"summary","type":"4cd48f3e5108bd6e","optional":false}],"indices":[]},
"8e611bf46821e500": {"kind":"object","properties":[{"name":"tool","type":"fcf427290c6213c2","optional":true},{"name":"toolName","type":"4cd48f3e5108bd6e","optional":false},{"name":"type","type":"85f9b505b7bdff1f","optional":false}],"indices":[]},
"8f324cea3916c051": {"kind":"object","properties":[{"name":"form","type":"16d0507cb94b4cfc","optional":false},{"name":"kind","type":"2f3f201c8846e19c","optional":false}],"indices":[]},
"8fcb0b44a575e998": {"kind":"object","properties":[{"name":"kind","type":"a720757f704a2512","optional":false}],"indices":[]},
"904bf499d98be4a3": {"kind":"union","types":["7382c4fc95fe8c5d","b765ee31033cea46","f6cb462f808309e3"]},
"938f7c7357bc403e": {"kind":"primitive","type":"null"},
"95a3ffccbf3c8afc": {"kind":"literal","value":"owner"},
"95c0e88fcf0bafb2": {"kind":"literal","value":"assistant"},
"9645cf93769a6dfb": {"kind":"literal","value":"user-question-reply"},
"96bfb916a727cd44": {"kind":"object","properties":[{"name":"form","type":"383cf73ae4748e5b","optional":false},{"name":"kind","type":"24b10f7cdae64928","optional":false}],"indices":[]},
"96f4a9c81fc0f940": {"kind":"union","types":["3e2ece4967cfebe9","62ecb62dc0fe1b5e"]},
"99135d8a39379779": {"kind":"object","properties":[{"name":"form","type":"aec150797b655c5f","optional":false},{"name":"kind","type":"9ac52f0f84fec2eb","optional":false}],"indices":[]},
"99e7e7d7fed55ae6": {"kind":"literal","value":"tool-jobs"},
"99f0e98184709ad1": {"kind":"object","properties":[{"name":"form","type":"aec150797b655c5f","optional":false},{"name":"kind","type":"bb94f3217aa56c88","optional":false}],"indices":[]},
"9ac52f0f84fec2eb": {"kind":"literal","value":"model-selection"},
"9edc162949abf93b": {"kind":"object","properties":[{"name":"deliveryId","type":"4cd48f3e5108bd6e","optional":false},{"name":"form","type":"e8de0f457fed5b9f","optional":false},{"name":"kind","type":"db6c5246911d47a7","optional":false},{"name":"provider","type":"4cd48f3e5108bd6e","optional":false},{"name":"ruleId","type":"4cd48f3e5108bd6e","optional":false},{"name":"source","type":"4cd48f3e5108bd6e","optional":false},{"name":"summary","type":"4cd48f3e5108bd6e","optional":false}],"indices":[]},
"9f65c43e340bba31": {"kind":"object","properties":[{"name":"form","type":"fee1caf8ab6a65c1","optional":false},{"name":"kind","type":"78a9be340090b735","optional":false}],"indices":[]},
"a13d8f379222ece8": {"kind":"literal","value":"tool"},
"a15a19ef63cf5d1b": {"kind":"object","properties":[{"name":"form","type":"16d0507cb94b4cfc","optional":false},{"name":"kind","type":"9ac52f0f84fec2eb","optional":false}],"indices":[]},
"a16e567c8cd2c55c": {"kind":"object","properties":[{"name":"form","type":"383cf73ae4748e5b","optional":false},{"name":"kind","type":"d9edf1862e4471d6","optional":false}],"indices":[]},
"a2066bd52f61218b": {"kind":"object","properties":[{"name":"kind","type":"2f3f201c8846e19c","optional":false}],"indices":[]},
"a3c6f1ea38e9a61d": {"kind":"object","properties":[{"name":"form","type":"e8de0f457fed5b9f","optional":false},{"name":"kind","type":"9ac52f0f84fec2eb","optional":false},{"name":"summary","type":"4cd48f3e5108bd6e","optional":false}],"indices":[]},
"a3e299483348f27a": {"kind":"object","properties":[{"name":"maxTokens","type":"0657d3ca5ac4e7bf","optional":false},{"name":"messageSeqs","type":"5d03ba38734809bc","optional":false},{"name":"messages","type":"78b9204976ab9faa","optional":false},{"name":"reasoningEffort","type":"4cd48f3e5108bd6e","optional":true},{"name":"route","type":"07e1a58c58b593f5","optional":false},{"name":"system","type":"4cd48f3e5108bd6e","optional":false},{"name":"titleProvider","type":"4cd48f3e5108bd6e","optional":false}],"indices":[]},
"a720757f704a2512": {"kind":"literal","value":"tool-registry"},
"a9059a81430b3207": {"kind":"literal","value":"hooks-claude-code"},
"aa2bf9e82b9fdeb9": {"kind":"object","properties":[{"name":"form","type":"fee1caf8ab6a65c1","optional":false},{"name":"kind","type":"f68c335306535a00","optional":false}],"indices":[]},
"aa93010d0e521ad7": {"kind":"primitive","type":"boolean"},
"aae343bc3d663c83": {"kind":"object","properties":[{"name":"form","type":"383cf73ae4748e5b","optional":false},{"name":"kind","type":"9ac52f0f84fec2eb","optional":false}],"indices":[]},
"aba175fb20ffc711": {"kind":"array","element":"83079c8a3f733ac3"},
"ada310bf0bdb8fed": {"kind":"object","properties":[{"name":"kind","type":"68f44f59e91c4139","optional":false}],"indices":[]},
"ade057f4fdc387ae": {"kind":"object","properties":[{"name":"form","type":"16d0507cb94b4cfc","optional":false},{"name":"kind","type":"99e7e7d7fed55ae6","optional":false}],"indices":[]},
"aec150797b655c5f": {"kind":"literal","value":"catalog"},
"b062078e899958f9": {"kind":"object","properties":[{"name":"kind","type":"1f1385dbc3e875c0","optional":false}],"indices":[]},
"b0b0eb1b16490f6b": {"kind":"object","properties":[{"name":"attachmentId","type":"4cd48f3e5108bd6e","optional":false},{"name":"bytes","type":"0657d3ca5ac4e7bf","optional":false},{"name":"height","type":"0657d3ca5ac4e7bf","optional":false},{"name":"mediaType","type":"2f1e6a7ebace43d7","optional":false},{"name":"name","type":"4cd48f3e5108bd6e","optional":true},{"name":"originalDimensions","type":"0f46153645c29784","optional":true},{"name":"width","type":"0657d3ca5ac4e7bf","optional":false}],"indices":[]},
"b2a5c8c008a31e3d": {"kind":"object","properties":[{"name":"form","type":"383cf73ae4748e5b","optional":false},{"name":"kind","type":"23af3759678a30de","optional":false}],"indices":[]},
"b2a948a5340a9341": {"kind":"object","properties":[{"name":"inserted","type":"5d00abf938272f22","optional":false},{"name":"outcome","type":"d4e4d575abeb4b72","optional":true},{"name":"removedCount","type":"0657d3ca5ac4e7bf","optional":true},{"name":"start","type":"0657d3ca5ac4e7bf","optional":false},{"name":"target","type":"96f4a9c81fc0f940","optional":false}],"indices":[]},
"b3681c026a0b98e9": {"kind":"object","properties":[{"name":"form","type":"e8de0f457fed5b9f","optional":false},{"name":"kind","type":"78a9be340090b735","optional":false},{"name":"summary","type":"4cd48f3e5108bd6e","optional":false}],"indices":[]},
"b36fd842cd004b53": {"kind":"object","properties":[{"name":"data","type":"b2a948a5340a9341","optional":false},{"name":"ignorable","type":"dacf3d5f3e4ff619","optional":true},{"name":"seq","type":"0657d3ca5ac4e7bf","optional":false},{"name":"time","type":"0657d3ca5ac4e7bf","optional":false},{"name":"type","type":"829a2fb3be6efd4b","optional":false}],"indices":[]},
"b4ec0af39fd52ed9": {"kind":"object","properties":[{"name":"kind","type":"bb94f3217aa56c88","optional":false}],"indices":[]},
"b52f215a6cd69105": {"kind":"array","element":"8064c03c87c357fd"},
"b613f83572fe2fbf": {"kind":"object","properties":[{"name":"form","type":"383cf73ae4748e5b","optional":false},{"name":"kind","type":"05205074b24d8340","optional":false},{"name":"through","type":"0657d3ca5ac4e7bf","optional":false}],"indices":[]},
"b6f94c17dd9bbd7c": {"kind":"object","properties":[{"name":"form","type":"fee1caf8ab6a65c1","optional":false},{"name":"kind","type":"bb94f3217aa56c88","optional":false}],"indices":[]},
"b70f3e81fe11b7a5": {"kind":"object","properties":[{"name":"form","type":"fee1caf8ab6a65c1","optional":false},{"name":"kind","type":"a9059a81430b3207","optional":false}],"indices":[]},
"b765ee31033cea46": {"kind":"literal","value":"set"},
"b782058cef83caf3": {"kind":"object","properties":[{"name":"content","type":"b52f215a6cd69105","optional":false},{"name":"id","type":"4cd48f3e5108bd6e","optional":false},{"name":"role","type":"68f44f59e91c4139","optional":false},{"name":"source","type":"7a5b68bd1a5118c8","optional":false,"compatibility":{"version":1,"policy":"session-source-attribution","binding":"session.user-message.source","discriminator":"kind","unknownKinds":"preserve","attributionKinds":["action-cards","tmux-context","user-question-reply","working-directory"]}}],"indices":[]},
"b79641a9d25e7566": {"kind":"object","properties":[{"name":"kind","type":"8067b0d232c350f1","optional":false}],"indices":[]},
"b815d7b91cdd9fc6": {"kind":"literal","value":"goal"},
"ba2a1a8f194b1bb2": {"kind":"object","properties":[{"name":"form","type":"fee1caf8ab6a65c1","optional":false},{"name":"kind","type":"05205074b24d8340","optional":false},{"name":"through","type":"0657d3ca5ac4e7bf","optional":false}],"indices":[]},
"ba4c40b4381e0dcd": {"kind":"object","properties":[{"name":"form","type":"6f9e00383836275e","optional":false},{"name":"kind","type":"23af3759678a30de","optional":false},{"name":"sections","type":"c748fb42c80a1200","optional":false}],"indices":[]},
"baccc97ec34be6c4": {"kind":"object","properties":[{"name":"form","type":"e8de0f457fed5b9f","optional":false},{"name":"kind","type":"0fe52387f6505270","optional":false},{"name":"summary","type":"4cd48f3e5108bd6e","optional":false}],"indices":[]},
"bb94f3217aa56c88": {"kind":"literal","value":"hooks-codex"},
"bbc5696e3f13c758": {"kind":"opaque","reason":"unknown"},
"bcf0caf62d964b2f": {"kind":"object","properties":[{"name":"endSeq","type":"0657d3ca5ac4e7bf","optional":false},{"name":"op","type":"f6cb462f808309e3","optional":false},{"name":"startSeq","type":"0657d3ca5ac4e7bf","optional":false}],"indices":[]},
"bf16d2e98a7f1389": {"kind":"literal","value":"subagent-settled"},
"c1f0f17b6493b85d": {"kind":"object","properties":[{"name":"arguments","type":"4cd48f3e5108bd6e","optional":false},{"name":"id","type":"4cd48f3e5108bd6e","optional":false},{"name":"name","type":"4cd48f3e5108bd6e","optional":false},{"name":"type","type":"1f0133217eeeffd8","optional":false}],"indices":[]},
"c5228b7a55498d84": {"kind":"object","properties":[{"name":"form","type":"6f9e00383836275e","optional":false},{"name":"kind","type":"99e7e7d7fed55ae6","optional":false},{"name":"sections","type":"c748fb42c80a1200","optional":false}],"indices":[]},
"c55827fba7018198": {"kind":"object","properties":[{"name":"callId","type":"4cd48f3e5108bd6e","optional":false},{"name":"kind","type":"9645cf93769a6dfb","optional":false},{"name":"outcome","type":"85d6d96ff8842974","optional":false}],"indices":[]},
"c5687b1313206a6f": {"kind":"literal","value":"cordis-host-runner"},
"c748fb42c80a1200": {"kind":"array","element":"86e1e384495ee09a"},
"cb1c8959e1726f82": {"kind":"object","properties":[{"name":"form","type":"16d0507cb94b4cfc","optional":false},{"name":"kind","type":"24b10f7cdae64928","optional":false}],"indices":[]},
"cda2345a9be9d139": {"kind":"object","properties":[{"name":"form","type":"e8de0f457fed5b9f","optional":false},{"name":"kind","type":"24b10f7cdae64928","optional":false},{"name":"summary","type":"4cd48f3e5108bd6e","optional":false}],"indices":[]},
"cda79b839d8cc0c7": {"kind":"union","types":["09f3f8dc417f2782","4b259f7f8f0a44ad","7580e7d96dc8475f","b782058cef83caf3","fa87537f4a9c1e25"]},
"d4e4d575abeb4b72": {"kind":"literal","value":"canceled"},
"d85590a5675c6e2f": {"kind":"object","properties":[{"name":"form","type":"aec150797b655c5f","optional":false},{"name":"kind","type":"1f1385dbc3e875c0","optional":false}],"indices":[]},
"d9c462b9f62fdd3e": {"kind":"object","properties":[{"name":"callId","type":"4cd48f3e5108bd6e","optional":false},{"name":"kind","type":"a13d8f379222ece8","optional":false}],"indices":[]},
"d9edf1862e4471d6": {"kind":"literal","value":"schedule"},
"dacf3d5f3e4ff619": {"kind":"literal","value":true},
"db06ef35b27a3842": {"kind":"object","properties":[{"name":"form","type":"6f9e00383836275e","optional":false},{"name":"kind","type":"1f1385dbc3e875c0","optional":false},{"name":"sections","type":"c748fb42c80a1200","optional":false}],"indices":[]},
"db6c5246911d47a7": {"kind":"literal","value":"webhook"},
"dba54c09c6e7aafc": {"kind":"object","properties":[{"name":"kind","type":"4edec1b770e8ec4c","optional":false}],"indices":[]},
"dd00cdccd0ba487b": {"kind":"object","properties":[{"name":"form","type":"aec150797b655c5f","optional":false},{"name":"kind","type":"05205074b24d8340","optional":false},{"name":"through","type":"0657d3ca5ac4e7bf","optional":false}],"indices":[]},
"dd5ee8762bd8a14b": {"kind":"object","properties":[{"name":"form","type":"aec150797b655c5f","optional":false},{"name":"kind","type":"99e7e7d7fed55ae6","optional":false}],"indices":[]},
"dd6cc2bb2a8f2824": {"kind":"object","properties":[{"name":"form","type":"383cf73ae4748e5b","optional":false},{"name":"kind","type":"99e7e7d7fed55ae6","optional":false}],"indices":[]},
"dde0cdf9d426768b": {"kind":"object","properties":[{"name":"kind","type":"05205074b24d8340","optional":false},{"name":"through","type":"0657d3ca5ac4e7bf","optional":false}],"indices":[]},
"de25352d2dc41963": {"kind":"literal","value":"user/message"},
"de30b1fa12c75f81": {"kind":"literal","value":"image/gif"},
"deaae5cd30987d4f": {"kind":"object","properties":[{"name":"form","type":"16d0507cb94b4cfc","optional":false},{"name":"kind","type":"0fe52387f6505270","optional":false}],"indices":[]},
"deebfc7c62177ea8": {"kind":"object","properties":[{"name":"kind","type":"95a3ffccbf3c8afc","optional":false}],"indices":[]},
"df843efd87c78cf8": {"kind":"array","element":"4f91f187baeabef5"},
"e438a9d4fb883b3e": {"kind":"literal","value":"append"},
"e7f4cbb3a57f4cbd": {"kind":"object","properties":[{"name":"form","type":"e8de0f457fed5b9f","optional":false},{"name":"kind","type":"05205074b24d8340","optional":false},{"name":"summary","type":"4cd48f3e5108bd6e","optional":false},{"name":"through","type":"0657d3ca5ac4e7bf","optional":false}],"indices":[]},
"e8de0f457fed5b9f": {"kind":"literal","value":"notice"},
"ebb768d85ec87fb6": {"kind":"literal","value":"image/webp"},
"edbbfff1128099fd": {"kind":"object","properties":[{"name":"form","type":"e8de0f457fed5b9f","optional":false},{"name":"kind","type":"a9059a81430b3207","optional":false},{"name":"summary","type":"4cd48f3e5108bd6e","optional":false}],"indices":[]},
"ee577a0cc18664c9": {"kind":"object","properties":[{"name":"form","type":"e8de0f457fed5b9f","optional":false},{"name":"kind","type":"8067b0d232c350f1","optional":false},{"name":"summary","type":"4cd48f3e5108bd6e","optional":false}],"indices":[]},
"efa1a091017ce533": {"kind":"object","properties":[{"name":"data","type":"a3e299483348f27a","optional":false},{"name":"ignorable","type":"dacf3d5f3e4ff619","optional":true},{"name":"seq","type":"0657d3ca5ac4e7bf","optional":false},{"name":"time","type":"0657d3ca5ac4e7bf","optional":false},{"name":"type","type":"587bb11da730e06a","optional":false}],"indices":[]},
"f19bee84bf3d2090": {"kind":"object","properties":[{"name":"attachmentId","type":"4cd48f3e5108bd6e","optional":false},{"name":"bytes","type":"0657d3ca5ac4e7bf","optional":false},{"name":"name","type":"4cd48f3e5108bd6e","optional":false}],"indices":[]},
"f230c0b583e23b25": {"kind":"object","properties":[{"name":"form","type":"6f9e00383836275e","optional":false},{"name":"kind","type":"8067b0d232c350f1","optional":false},{"name":"sections","type":"c748fb42c80a1200","optional":false}],"indices":[]},
"f29d63336e8266d8": {"kind":"object","properties":[{"name":"kind","type":"d9edf1862e4471d6","optional":false}],"indices":[]},
"f2da0f27c1e6add5": {"kind":"object","properties":[{"name":"attachment","type":"f19bee84bf3d2090","optional":false},{"name":"type","type":"277b61f9c6f81704","optional":false}],"indices":[]},
"f4a66f654996c093": {"kind":"object","properties":[{"name":"kind","type":"0fe52387f6505270","optional":false}],"indices":[]},
"f56c5b59ca016131": {"kind":"object","properties":[{"name":"form","type":"383cf73ae4748e5b","optional":false},{"name":"kind","type":"7f13860ee89a2d35","optional":false},{"name":"references","type":"df843efd87c78cf8","optional":false},{"name":"version","type":"f88f1b7b6bb733f6","optional":false}],"indices":[]},
"f6329fb30c7ea801": {"kind":"object","properties":[{"name":"form","type":"383cf73ae4748e5b","optional":false},{"name":"kind","type":"0fe52387f6505270","optional":false}],"indices":[]},
"f6605d6c57933aea": {"kind":"object","properties":[{"name":"form","type":"fee1caf8ab6a65c1","optional":false},{"name":"kind","type":"9ac52f0f84fec2eb","optional":false}],"indices":[]},
"f68c335306535a00": {"kind":"literal","value":"user-approval"},
"f6afe41e3926f422": {"kind":"object","properties":[{"name":"form","type":"fee1caf8ab6a65c1","optional":false},{"name":"kind","type":"8067b0d232c350f1","optional":false}],"indices":[]},
"f6cb462f808309e3": {"kind":"literal","value":"replace"},
"f7283223aacdc39b": {"kind":"object","properties":[{"name":"kind","type":"9ac52f0f84fec2eb","optional":false}],"indices":[]},
"f88f1b7b6bb733f6": {"kind":"literal","value":1},
"f97f47b0eb87905e": {"kind":"object","properties":[{"name":"form","type":"16d0507cb94b4cfc","optional":false},{"name":"kind","type":"78a9be340090b735","optional":false}],"indices":[]},
"fa87537f4a9c1e25": {"kind":"object","properties":[{"name":"content","type":"b52f215a6cd69105","optional":false},{"name":"id","type":"4cd48f3e5108bd6e","optional":false},{"name":"role","type":"95c0e88fcf0bafb2","optional":false},{"name":"source","type":"719c51f67074f970","optional":false}],"indices":[]},
"fb8b6a2e53cb9cf4": {"kind":"literal","value":"image"},
"fbd2c74b346a1595": {"kind":"literal","value":"agent-instructions"},
"fc2ae6962f9f8801": {"kind":"object","properties":[{"name":"entries","type":"aba175fb20ffc711","optional":false},{"name":"form","type":"aec150797b655c5f","optional":false},{"name":"kind","type":"6ebcc2dbec9f2dab","optional":false},{"name":"update","type":"dacf3d5f3e4ff619","optional":true}],"indices":[]},
"fcf427290c6213c2": {"kind":"primitive","type":"never"},
"fcf81d3c58c13593": {"kind":"object","properties":[{"name":"form","type":"383cf73ae4748e5b","optional":false},{"name":"kind","type":"78a9be340090b735","optional":false}],"indices":[]},
"fe1a7acdeed4d0f5": {"kind":"object","properties":[{"name":"form","type":"e8de0f457fed5b9f","optional":false},{"name":"kind","type":"1f1385dbc3e875c0","optional":false},{"name":"summary","type":"4cd48f3e5108bd6e","optional":false}],"indices":[]},
"fee1caf8ab6a65c1": {"kind":"literal","value":"instructions"}
}
}
@@ -0,0 +1,60 @@
---
description: "记录持久化类型更改及其兼容性确认。"
kind: persistence-change
---
# 2026-10-22-action-card-status
[English](2026-10-22-action-card-status.md) | 中文
## 概述
为写入日志的模型上下文新增操作卡片状态的来源标识。
## 目录
- [声明](#declaration)
- [兼容性](#compatibility)
- [验证](#verification)
- [开发备注](#dev-note)
<a id="declaration"></a>
## 声明
```yaml persistence-change
schemaVersion: 1
id: 2026-10-22-action-card-status
baseline: false
changes:
- root: "event:agent/inbox/spliced"
previous: "2026-10-13-user-rpc-principal"
after: "b36fd842cd004b532ce96fd5d6f5be28e017eda043131aa3cf5e69edc436108e"
decision: same-version
- root: "event:developer/message"
previous: "2026-10-13-user-rpc-principal"
after: "5ef58f2a1bdc576c1f12fa1f4da1920ece020b13c0f991a578cddcbeb28bc5be"
decision: same-version
- root: "event:session/title-llm-request"
previous: "2026-10-13-user-rpc-principal"
after: "efa1a091017ce533e38b0ef3e14d11a5c102ff1793229b18dad719a4268d9adc"
decision: same-version
- root: "event:user/message"
previous: "2026-10-13-user-rpc-principal"
after: "58b122df513aeaf7ddc5783dbb1fd844e2979bb273964e30ae351a8db6510019"
decision: same-version
```
<a id="compatibility"></a>
## 兼容性
已有消息和记录不变。没有操作卡片插件的读取方保留新的 action-cards 来源类型及其 JSON 元数据,并把记录的文本当作普通上下文。只有写入方使用 through 序号确认已送达的决定;它不授予任何权限,也不改变回放的解释。
<a id="verification"></a>
## 验证
对 action-cards、creation-recommendations、client-ui-action-cards 和 client-ui-creation-recommendations 的测试运行 pnpm exec vitest run:120 个测试通过,源码覆盖率 100%。Web creation-recommendations 回放在下一次回答之前记录了已忽略卡片的状态通知。
<a id="dev-note"></a>
## 开发备注
无。
@@ -15,8 +15,8 @@
en: 14c57f30e676f6e3
zh: b1a0ccf26fab3727
/session-persistence-formats/format-references:
en: 17ef09dfc5a89073
zh: 59b8d60acb66c2dc
en: e3511a4ac3bc4457
zh: 828f3c39f80d5370
/session-persistence-formats/scope-and-evidence:
en: b220062139ddac1e
zh: 7c4c0efe05dafa9c
@@ -32,7 +32,7 @@ The index is generated from validated snapshots and the current writer constant.
| 1 | PR #3349 | [V1](v1.md) | [JSON](v1.schema.json) | 54 / 415 |
| 2 | `dsh-v0.1.3-alpha.2` | [V2](v2.md) | [JSON](v2.schema.json) | 56 / 435 |
| 3 | PR #4320 | [V3](v3.md) | [JSON](v3.schema.json) | 60 / 467 |
| 4 | Current checkout | [Current catalog](../../persistence-catalog.md) | [JSON](../../persistence-schema.json) | 65 / 614 |
| 4 | Current checkout | [Current catalog](../../persistence-catalog.md) | [JSON](../../persistence-schema.json) | 65 / 622 |
<!-- persistence-format-index:end -->
@@ -32,7 +32,7 @@ description: "查找从 V0 到工作区写入器版本的每个 Session 格式
| 1 | PR #3349 | [V1](v1.zh.md) | [JSON](v1.schema.json) | 54 / 415 |
| 2 | `dsh-v0.1.3-alpha.2` | [V2](v2.zh.md) | [JSON](v2.schema.json) | 56 / 435 |
| 3 | PR #4320 | [V3](v3.zh.md) | [JSON](v3.schema.json) | 60 / 467 |
| 4 | 当前工作树 | [当前目录](../../persistence-catalog.zh.md) | [JSON](../../persistence-schema.json) | 65 / 614 |
| 4 | 当前工作树 | [当前目录](../../persistence-catalog.zh.md) | [JSON](../../persistence-schema.json) | 65 / 622 |
<!-- persistence-format-index:end -->
+46 -30
View File
@@ -5,7 +5,7 @@
{"key":"JsonlHeaderLine","kind":"header","digest":"18ee62b8900a4c3d046700f05d7a4d49d6cab2a660a020c87481d1603dd8bd4f"},
{"key":"SessionEventEnvelope","kind":"envelope","digest":"5776e5553ff2dfe3f5bc202dbb1e7c9f93e35a531aebb7764c23b2b6153b2ccc"},
{"key":"event:agent-preset/selected","kind":"event","event":"agent-preset/selected","surface":false,"digest":"a10c17474eaf2ddab7095a099e0fe3d046fc18e56c3e344fc8894c05ff9ef97b"},
{"key":"event:agent/inbox/spliced","kind":"event","event":"agent/inbox/spliced","surface":false,"digest":"0755b7d1128319ba2e389619cbbf2c961ad648882b4e91da7f3e9582cc8a2034"},
{"key":"event:agent/inbox/spliced","kind":"event","event":"agent/inbox/spliced","surface":false,"digest":"b36fd842cd004b532ce96fd5d6f5be28e017eda043131aa3cf5e69edc436108e"},
{"key":"event:approval/asked","kind":"event","event":"approval/asked","surface":false,"digest":"3bfeb47b58606f4661904bc723da612782214c463d01e6d61cd6d6193d7374e1"},
{"key":"event:approval/decided","kind":"event","event":"approval/decided","surface":false,"digest":"bb1ab3d08f49a9f3b265f844cd78d5c49813062a7b34b54904b426f85d0ff6e3"},
{"key":"event:approval/policy","kind":"event","event":"approval/policy","surface":false,"digest":"26718e15e7e395bce9642dba5bbe09b3b1a4ce2213d20d566cd9207d7fc5fb78"},
@@ -18,7 +18,7 @@
{"key":"event:compaction/start","kind":"event","event":"compaction/start","surface":false,"digest":"db874d463b0fdec77e9da1c4568f37cb70bd6596781eb93800db44fb8a116965"},
{"key":"event:compaction/summary","kind":"event","event":"compaction/summary","surface":false,"digest":"e2f9a41e0989f54ed8cee80f8db2bcf9d60a5c810dc9d45b83fa050b9dce7602"},
{"key":"event:deliverables/presented","kind":"event","event":"deliverables/presented","surface":false,"digest":"13d3d180f977bf78081d487ffa0ecb75857349bcab29a5a3fb48189fca2a6176"},
{"key":"event:developer/message","kind":"event","event":"developer/message","surface":true,"digest":"97e0162f983c27456ec71269fb4976b1851588b8c1aeac5d071d43e11f95e03c"},
{"key":"event:developer/message","kind":"event","event":"developer/message","surface":true,"digest":"5ef58f2a1bdc576c1f12fa1f4da1920ece020b13c0f991a578cddcbeb28bc5be"},
{"key":"event:feedback/message-delete","kind":"event","event":"feedback/message-delete","surface":false,"digest":"3ee93b06f3a125850337602bcdf155d2538c43a5c944ec55b1b3c365152d6796"},
{"key":"event:feedback/message-put","kind":"event","event":"feedback/message-put","surface":false,"digest":"b5086d249e8502e9ead1d39156bb8d559bde7951cac0f14ce150345b4e42a2bf"},
{"key":"event:feedback/record","kind":"event","event":"feedback/record","surface":false,"digest":"b54940ff095c17e874c5be03815f4c2145a256cf3a1d34dae4ab2f7769dfffe8"},
@@ -40,7 +40,7 @@
{"key":"event:session-log-deepseek/delivery-accepted","kind":"event","event":"session-log-deepseek/delivery-accepted","surface":false,"digest":"d63b8b8ffad9c02fd80c43a17df4f240c1fe8118ecca9de34f9d5871838ab5b9"},
{"key":"event:session/end-seed","kind":"event","event":"session/end-seed","surface":false,"digest":"5e6db6e24948d4a853c71cb9fabd252ad051ce93d4672c1266cf837c1c17b84e"},
{"key":"event:session/title","kind":"event","event":"session/title","surface":false,"digest":"1b912703e2d64f91c99c675b8f805b01076c8325b905c1218ad81ef0b24909d5"},
{"key":"event:session/title-llm-request","kind":"event","event":"session/title-llm-request","surface":false,"digest":"79a4c87f8bfb4d6d05cd9952b99210d6a7db3b478c4a01d94b4c50602308c056"},
{"key":"event:session/title-llm-request","kind":"event","event":"session/title-llm-request","surface":false,"digest":"efa1a091017ce533e38b0ef3e14d11a5c102ff1793229b18dad719a4268d9adc"},
{"key":"event:step/end","kind":"event","event":"step/end","surface":false,"digest":"e0a787e6ec76c7c94fecbc501b489164ab0293db05bc947914077ad01e674f05"},
{"key":"event:step/start","kind":"event","event":"step/start","surface":false,"digest":"4513e088d43e6c68425be30451b9f961cc264fe7318ca62681c41d4d78615986"},
{"key":"event:subagent/catalog","kind":"event","event":"subagent/catalog","surface":false,"digest":"53750b9abf61efb7992b0aadc3e247ec927449c63266df28230fd1139f5590ac"},
@@ -62,24 +62,22 @@
{"key":"event:tool/result","kind":"event","event":"tool/result","surface":true,"digest":"7c9f44e90a0058f4cc532ae20dad0c10afa6eba22e70a6c79fc79490bad64397"},
{"key":"event:turn/end","kind":"event","event":"turn/end","surface":false,"digest":"0f8512903d94f57a4748fa1a2092e64342856796684e6b8343db685b192745ce"},
{"key":"event:turn/start","kind":"event","event":"turn/start","surface":false,"digest":"aa0957eca50aeb28bcd2e6930b95809926edacb550c8c340ba526ba6b861b3d8"},
{"key":"event:user/message","kind":"event","event":"user/message","surface":true,"digest":"2ae8594a998c9862a597129e782277bcc518266712a703b5c3161bdf552d13b8"},
{"key":"event:user/message","kind":"event","event":"user/message","surface":true,"digest":"58b122df513aeaf7ddc5783dbb1fd844e2979bb273964e30ae351a8db6510019"},
{"key":"event:web/deepseek-search-llm-request","kind":"event","event":"web/deepseek-search-llm-request","surface":false,"digest":"0c556c9b9f2a96238a6b9bbb99e748206cbff2ed6203041c9571f04ae5802f52"},
{"key":"event:working-directory/change","kind":"event","event":"working-directory/change","surface":false,"digest":"124aa62078ca3311353b39cc3bf6c75da281338be6bbf672f8b361d828cddca3"},
{"key":"event:workspace/changes","kind":"event","event":"workspace/changes","surface":false,"digest":"e308ccf867a5398e316e0af8cb6ce238a8d33a63b9b384c8250a686786285f72"}
],
"types": [
{"digest":"00c054d3018c198d92f459563331edd1385e58345c677bb499791df2da0ade9f","names":["packages/session/session-title-llm/src/index.ts#SessionTitleLlmRequestEventData"],"sources":["packages/session/session-title-llm/src/index.ts:37"]},
{"digest":"0116b1f89c05221d56b0ba35c83d302bf4857bc7277d247e9bbaf74b27d2c66e","names":[],"sources":[]},
{"digest":"0203243b72fc867afc81aff5f4cf2167e564ed342534eb612fc92f295cf07789","names":[],"sources":[]},
{"digest":"022623b9ef98ec47ec3c3bae0c51027c70d2601141c5e92e26a0d83d1b4cef06","names":[],"sources":[]},
{"digest":"023a355b2be0d688fb6e4df8388dadb942761d46cfe96d0f499aae9631a159d4","names":[],"sources":["packages/core/session/src/types.ts:207"]},
{"digest":"02b2c23f2d94b028c9588a8faace3fce1217405db346da3389368b495457bf79","names":[],"sources":["packages/extensions/cordis-host-runner/src/index.ts:14"]},
{"digest":"0341d4c3dfcd53253cdc1e789d9130d3cd848b6f8b0706bf92f313a68b39ff23","names":[],"sources":[]},
{"digest":"040cd65423bd64264d225504f1c572d239eeb1793d0c2856ad8e42bb6e1e148b","names":["packages/llm/llm/src/message.ts#Message"],"sources":["packages/llm/llm/src/message.ts:196"]},
{"digest":"05205074b24d8340cfd7995f6cb64779c7f1241490954700115601e264178877","names":[],"sources":[]},
{"digest":"05a2796621b1ee6c03ef7abe560b25116bb4da4d592312117242a78415a32b04","names":[],"sources":[]},
{"digest":"05d2ae1e0c7f8770f83f9a09047afb39a335d8ac986df3193659aa1e581472cf","names":["packages/interaction/user-approval/src/index.ts#ApprovalPolicy"],"sources":["packages/interaction/user-approval/src/index.ts:68"]},
{"digest":"0657d3ca5ac4e7bf79b00bcf860b1c7159d01a9da137497221ec2f1776e365f7","names":["packages/core/session/src/types.ts#SessionSeq"],"sources":["packages/core/session/src/types.ts:33"]},
{"digest":"0755b7d1128319ba2e389619cbbf2c961ad648882b4e91da7f3e9582cc8a2034","names":[],"sources":[]},
{"digest":"07e1a58c58b593f507b8e7f25723bb0aa42baae6e00f5ecfddf28cb1e64d3235","names":["packages/session/session-title/src/types.ts#SessionTitleModelIdentity","packages/subagent/tool-subagent/src/model-selection.ts#AllowedModelRoute"],"sources":["packages/session/session-title/src/types.ts:20","packages/subagent/tool-subagent/src/model-selection.ts:9"]},
{"digest":"07eccc9ea2f9b5439d76cc77d5e062936677d43f07a23da749819b0a22ee36b7","names":[],"sources":[]},
{"digest":"09655b34d7f3cf1e9005974de2357de2d4a60deea165d16dac1eadaa6ec539a8","names":[],"sources":[]},
@@ -101,7 +99,6 @@
{"digest":"0ed8d1108fe7a5e1c3078ef1faea12199a56b6b1216ccaceb0f6103b0c9a7691","names":[],"sources":[]},
{"digest":"0eeb17e1df89b1ebc2902116fe7145e0d321ef0db26e6bdebc802cb8513a01c1","names":[],"sources":[]},
{"digest":"0f46153645c297846a3fad3911636659ca4717a5e7d71c9f50bffacd312115ef","names":[],"sources":["packages/attachment/attachment/src/types.ts:28"]},
{"digest":"0f6265188dae7ba6faa26830a180bb4deb0760561f97ee0c9d399ccf6c9cf1f7","names":["packages/llm/llm/src/message.ts#DeveloperMessage"],"sources":["packages/llm/llm/src/message.ts:157"]},
{"digest":"0f8512903d94f57a4748fa1a2092e64342856796684e6b8343db685b192745ce","names":[],"sources":[]},
{"digest":"0fe52387f65052702ed9d7cd59db826468936595d1b3eec207e7a823adc719db","names":[],"sources":[]},
{"digest":"1033093edd0db80ff410e00830b523405e00bb0c7684948e531ff65095799625","names":[],"sources":[]},
@@ -178,11 +175,9 @@
{"digest":"2aad1179f957631477b582e57cd8f538cb82618fc3fb816bde1171154235b065","names":[],"sources":[]},
{"digest":"2ab12a0e345658f9342816d773fe610f049982bcad82631f43b9205abe43e5ee","names":[],"sources":[]},
{"digest":"2abb711460e6238d851c30f3e4bdd87a2aa1887ee70575b3d3b7b25610673095","names":[],"sources":["packages/llm/llm/src/types.ts:473"]},
{"digest":"2ae8594a998c9862a597129e782277bcc518266712a703b5c3161bdf552d13b8","names":[],"sources":[]},
{"digest":"2aed07493b1c65b6d00de5586da1e743cf501684b9ad43a1765fb4007d4ea456","names":[],"sources":[]},
{"digest":"2c4c21f19f426a58f2dac914e34313aa43f8408972de857a486d1f086d5adb6c","names":[],"sources":[]},
{"digest":"2c64d4bc7264fd3f8bed47e96ab8a593bce05a1e4e1c436dcd244a3b7e0c2a9d","names":[],"sources":[]},
{"digest":"2cf075b013d363ad743b5a4982900678d108499c721bc11255137402003db711","names":["packages/llm/llm/src/message.ts#UserMessage"],"sources":["packages/llm/llm/src/message.ts:162"]},
{"digest":"2d11ca7b0d4493e244b74eba093227866b33249841e59a1e9bf56afed38604c3","names":[],"sources":["packages/web/web-search-deepseek/src/provider.ts:71"]},
{"digest":"2da725e554cd89301d829ed9dc45a572c3cd1b93b5e91d0d71f007976d8a0863","names":[],"sources":[]},
{"digest":"2e3523541e942ef6cc9fbc5b91f242e0ca4c6ada479b79b96025b797f6e41f3f","names":["packages/schedule/schedule/src/types.ts#ScheduleChange"],"sources":["packages/schedule/schedule/src/types.ts:256"]},
@@ -194,6 +189,7 @@
{"digest":"30d5a537b296685770b1241c4b4bbde19d5a31ff81aa23df73fab5e90b8fc65f","names":["packages/session/session-title/src/types.ts#SessionTitleSource"],"sources":["packages/session/session-title/src/types.ts:28"]},
{"digest":"312aee2f0c42aabbae6cc60d92d596d8453c0aeb061c5868d1755a69ef49e69a","names":[],"sources":[]},
{"digest":"31907de9563751822af6d9be02bafaf26faa098bbd1cb412524134d577cc1648","names":["packages/compaction/compaction/src/checkpoint.ts#CompactionCheckpointSource"],"sources":["packages/compaction/compaction/src/checkpoint.ts:22"]},
{"digest":"3247e566f4340029325b70e4366b56fa9719977f1e39033b769a2ec221ab22a9","names":[],"sources":[]},
{"digest":"326a9bfda27dda5882fb3cbe6e715df41dae2298fdae3d66437457268a665693","names":["packages/llm/llm/src/call-config.ts#LlmCallConfigAdapterDefaults"],"sources":["packages/llm/llm/src/call-config.ts:54"]},
{"digest":"3305a4bb6b6c29b482ba91d59c60b47e8f98282d3dacc8c29e4a4c2712209bdd","names":[],"sources":[]},
{"digest":"33341aac09312300de5a178e6cc6f6544a4876f7f710cfea55eda473c8d329ef","names":["packages/llm/llm/src/assistant-stream.ts#AssistantStreamRecord"],"sources":["packages/llm/llm/src/assistant-stream.ts:20"]},
@@ -236,7 +232,7 @@
{"digest":"44818000e56acb97afdb5045ce235eb55e3fb1ccabc9fc7d0750bb38f36a7f8e","names":[],"sources":[]},
{"digest":"44c8c77aa9aa100b0d74d2deee85474b29faa6eee0ac0f662f7abe9931ddd7f2","names":[],"sources":[]},
{"digest":"4513e088d43e6c68425be30451b9f961cc264fe7318ca62681c41d4d78615986","names":[],"sources":[]},
{"digest":"454a0fa879e38076bdcdbd87ef561cd67d7e43e58a287cf7dbaa7b6cf7847020","names":[],"sources":[]},
{"digest":"45f005e6bbb7645248be792b715a74bc7fdf5a67f5381655d3a75dcc7aa2112e","names":[],"sources":[]},
{"digest":"45f2a07ea94573123b6ae14db787a40062dec3834f36d8be92c523d40ba28448","names":[],"sources":["packages/interaction/user-approval/src/types.ts:44"]},
{"digest":"477614bca595a9e7adb5a1870be446b9a8684f3ba8950259d6289f2d3b74f028","names":[],"sources":["packages/compaction/compaction-image-offload/src/projection.ts:25"]},
{"digest":"47e5d84f1a93450a78c7f3954b43990d0f629af7c034a56313e5deb63e772c1b","names":[],"sources":[]},
@@ -285,17 +281,18 @@
{"digest":"5808e36c95355f0e745201b5db1b6bcec4ebf5adb75d3e82d7d59cc89635ab80","names":["packages/llm/llm/src/types.ts#FinishReason"],"sources":["packages/llm/llm/src/types.ts:165"]},
{"digest":"587bb11da730e06aa52b62c087d217d6f8f22459b25f31cb9e1e1baf17944e5e","names":[],"sources":[]},
{"digest":"589d0d91239681f89ee485459dc8c2f73ea47183b5534289d966637024a2c35d","names":[],"sources":[]},
{"digest":"58b122df513aeaf7ddc5783dbb1fd844e2979bb273964e30ae351a8db6510019","names":[],"sources":[]},
{"digest":"58f94390b982f8a5ac1f4f235ba612557f4a1dc4a995d8b1cec63ce7639decb0","names":[],"sources":[]},
{"digest":"5abab3a27fd2736660ecddd94b28d6bbf114986dd894880153bd1be3ccacb563","names":[],"sources":[]},
{"digest":"5b232a9b9bfcc0fc6794dab7865bcf203a65785c5de5aa647a51228bff628413","names":[],"sources":[]},
{"digest":"5b6fb1f226ff56402db08a6aae82af76100b14056271875f69ec526a1ae05d51","names":[],"sources":["packages/interaction/commands/src/types.ts:112"]},
{"digest":"5bb69b88517f6214f2af4d1a6d2b771e271ad3ad695cc9c2c272069ae698c9bc","names":[],"sources":[]},
{"digest":"5bc2d7f02503fc6b3e1bb7e0b7fa22c0b9e4b9841fa3175b2ad8e607ae3525a8","names":["packages/llm/llm/src/message.ts#MessageSource"],"sources":["packages/llm/llm/src/message.ts:136"]},
{"digest":"5bc66269cfaf614c0ecda8ecb469f23101a7609793fc2b693ac23c4aecdb575e","names":[],"sources":[]},
{"digest":"5bd310104cfe448e530beb11422d2f791e2e32e8e95fdc528a15f9ca693dae75","names":[],"sources":[]},
{"digest":"5c45bf4c544a7211dcd8ba6ba7e5f1bc39b49e7a9df9d5cbdc8e87c22771b37b","names":[],"sources":[]},
{"digest":"5cb63db9fe57dc09c3a1c2b84c3edf73ff5b62960155d8436a2377a5c43ce32c","names":[],"sources":[]},
{"digest":"5cdbf342b7748d152bc987b68f58e565e5bba4b227e076e2d2120d87662e45cd","names":[],"sources":[]},
{"digest":"5d00abf938272f22eb47199e9d40801e8d8ec4c88e2b138b15aa2a5a5bf87520","names":[],"sources":[]},
{"digest":"5d03ba38734809bcbd2a55221bd938b3b3fc34b49bb686c6113e9e4931e3aa78","names":[],"sources":[]},
{"digest":"5d17f27a1ef9f7026c0232714adf7573961536d75f3303ca97973975f5af170e","names":[],"sources":[]},
{"digest":"5d542ac7bdf13310c46e1a884dec9fb18ad8c6e153cff207e639cd7bf9d7e694","names":[],"sources":[]},
@@ -303,6 +300,7 @@
{"digest":"5dd766ccd1702a1142ad8b4bd44f2242f8e8a6752669ad3de0a5d224329b1438","names":[],"sources":["packages/compaction/compaction/src/types.ts:82"]},
{"digest":"5e6db6e24948d4a853c71cb9fabd252ad051ce93d4672c1266cf837c1c17b84e","names":[],"sources":[]},
{"digest":"5ebce1317eacd2c148456627a09668e927ee15c204f0643f5ea76b46b54a2647","names":[],"sources":[]},
{"digest":"5ef58f2a1bdc576c1f12fa1f4da1920ece020b13c0f991a578cddcbeb28bc5be","names":[],"sources":[]},
{"digest":"5f2117d1856a5ab43c28371faa978ebfaa48f8c7d734ea3fc94842210df83dca","names":["packages/llm/llm/src/types.ts#LlmFailure"],"sources":["packages/llm/llm/src/types.ts:41"]},
{"digest":"5f26a6c20b37632f8f57729d171c671def4683d994ac6257a8dffcd855101627","names":[],"sources":[]},
{"digest":"5f758f3de5f00c18968a7c6d347083aac5c9ae13f6a79c78f6611b23c4973564","names":["packages/subagent/subagent/src/descriptor.ts#OneShotSubagentDescriptorData"],"sources":["packages/subagent/subagent/src/descriptor.ts:61"]},
@@ -351,16 +349,19 @@
{"digest":"71bb3254ec8f1e491ce63b762a7b5f18709de23aa4e8abcc8b886f4b6ce051dd","names":[],"sources":["packages/llm/llm/src/assistant-stream.ts:28"]},
{"digest":"72ec79127a9c0d0241b1106a74cc2b24c81ce467170f1c3c93f7b71a9496d227","names":[],"sources":[]},
{"digest":"7382c4fc95fe8c5db65ca4d9da572b115d0dbb818b7f32c22ab040318ffbb4bc","names":[],"sources":[]},
{"digest":"7404c6123fdaa591474a37979a5a8ae70d1387af7c9f09bfe568b218935a974d","names":[],"sources":["packages/core/session/src/types.ts:313"]},
{"digest":"750a2b129d67704df675c06823c20c80b65cbae89d5aaa914fd5a72e3df395b5","names":["packages/jobs/jobs/src/types.ts#JobSettleCause"],"sources":["packages/jobs/jobs/src/types.ts:247"]},
{"digest":"7554565be416b0342053ee6a46256c9dab07c487349b4a5f10bab1edc151c619","names":[],"sources":[]},
{"digest":"756b5e23a5cb2c7f486f366a828f7461c88902af21125bd6810881f4ccbff6cc","names":[],"sources":[]},
{"digest":"7580e7d96dc8475fffe813a42af499665de3d4517fcdb3bc2337d0340e98d616","names":["packages/llm/llm/src/message.ts#DeveloperMessage"],"sources":["packages/llm/llm/src/message.ts:157"]},
{"digest":"7624ca28c61ca0eaa4f015140f3cea6e0c16cce2f2960fab5f531203d253903e","names":[],"sources":[]},
{"digest":"763c8a20af487263a0080548274f7437ef4a86e0ba8769127d1ced10a72c664d","names":[],"sources":[]},
{"digest":"76632533f67879dcb346fcae640540dbc89b2472b0bce88c59f7e48fc5c0e668","names":[],"sources":[]},
{"digest":"78a9be340090b735a39980e7222ce730c972fbcf1de7c9e0aef2125884b7caee","names":[],"sources":[]},
{"digest":"78b9204976ab9faaf133c3488b73d1dfb6f1a4bb6c0903e3473cd41a5e1e39e1","names":[],"sources":[]},
{"digest":"79650cfa10aa3c52440328d85e8a25f0ffadc8d7efd784e3c0819d8bb74a1985","names":[],"sources":["packages/core/session/src/types.ts:214"]},
{"digest":"79a4c87f8bfb4d6d05cd9952b99210d6a7db3b478c4a01d94b4c50602308c056","names":[],"sources":[]},
{"digest":"79ddc907312f97cf9553d4841e8b3fc937097bd8d97900a790efad3c05255e99","names":[],"sources":["packages/todo/tool-todo/src/types.ts:33"]},
{"digest":"7a5b68bd1a5118c827df9e2466acc5e4d16b83e3f97a2304d992958647592690","names":["packages/llm/llm/src/message.ts#MessageSource"],"sources":["packages/llm/llm/src/message.ts:136"]},
{"digest":"7a651f3b672c52fb615f22c0ce9cb3cc5b22aad92e6bbeefb0808fd7b9233a50","names":[],"sources":[]},
{"digest":"7af85bf70d4eafce63e739adae38ea85501e68df3a0e22f97f3a0be90fd9e4cb","names":[],"sources":[]},
{"digest":"7b1ce60193fa8b69292e5843858d9cee7dc565dcb40d33c136e5d7e8b8d4296a","names":[],"sources":[]},
@@ -431,7 +432,6 @@
{"digest":"9645cf93769a6dfb227bd400bd6cb9fa7bab9140f4a851e4ae92514ade6e3114","names":[],"sources":[]},
{"digest":"96bfb916a727cd4445a594df264c523fc039749a31bfed924b8cc7bb308a5bb0","names":[],"sources":[]},
{"digest":"96f4a9c81fc0f940ef71528a8cc66731ae4cf1f40d79680c5fe3c39f96d7723c","names":["packages/core/agent/src/types.ts#InboxTarget"],"sources":["packages/core/agent/src/types.ts:40"]},
{"digest":"97e0162f983c27456ec71269fb4976b1851588b8c1aeac5d071d43e11f95e03c","names":[],"sources":[]},
{"digest":"9890113e97dd3541787bfe6704f2b223d3a890b5325ca0acae2b0398909b413e","names":[],"sources":["packages/sandbox/sandbox-policy/src/session-mode.ts:33"]},
{"digest":"99135d8a3937977955eaac5f8f04f930dcd1562f8450a54a51cde9b921dd59a6","names":[],"sources":[]},
{"digest":"9947aa5938fe19a895298da412917acbf9ffb5093a73ab8baa257e6ae9e63a07","names":[],"sources":[]},
@@ -464,6 +464,7 @@
{"digest":"a2be0f0cb59f0d4a06557d45847d1938aced238baa77a6da416e590b8b958553","names":[],"sources":[]},
{"digest":"a3a259a1aa5401af786ce08c4a8419681d3000d73467991b5fc563389bc4ebd2","names":[],"sources":[]},
{"digest":"a3c6f1ea38e9a61d7a9706057ba65aabe02dfe6d9dd90d4736aa62325d874531","names":[],"sources":[]},
{"digest":"a3e299483348f27a4787a36bb3f29cd86024c65336abc10bb3d687468302c4cf","names":["packages/session/session-title-llm/src/index.ts#SessionTitleLlmRequestEventData"],"sources":["packages/session/session-title-llm/src/index.ts:37"]},
{"digest":"a3edd1efd1fef21b72c55e213c8a8b24196b091abde2b89db52be0b2aaae3cfa","names":[],"sources":[]},
{"digest":"a40d12070f6f4a124f32fb3cc86e7857554702e32f0eda3080a25c4b9ac9b18c","names":[],"sources":["packages/experimental/agent-team/src/types.ts:232"]},
{"digest":"a6567ccb2e530606b775371eb4fa31468d72084339968e8b0440a516a23b39dc","names":[],"sources":[]},
@@ -476,7 +477,6 @@
{"digest":"a8a2b9011fc067862df7ce40748868467e5778283d7ac193c13f77684a914c57","names":[],"sources":[]},
{"digest":"a9059a81430b320792bbb7c2ee1f8f6c77ff71e40ee44c18aec72e157c522694","names":[],"sources":[]},
{"digest":"a926dc71814edd01a4f041eb5fc766d153db3407ef4704e98f4c1b6527309ebb","names":["packages/schedule/schedule/src/types.ts#LegacyEveryScheduleRecord"],"sources":["packages/schedule/schedule/src/types.ts:173"]},
{"digest":"a927d444c928ac0efd9dd8b2e7d7d04d711c264cb71578d81e0e2fd143ff669f","names":[],"sources":[]},
{"digest":"a99ec0993c62c058e50af9f68922202a3b54f5e259bc2abf1915a73381d2316d","names":[],"sources":[]},
{"digest":"a99f76fd149051c363f960bfeeb5b9509587ecaf7591a42797e4bfe810d8987a","names":["packages/todo/tool-todo/src/types.ts#TodoItem"],"sources":["packages/todo/tool-todo/src/types.ts:23"]},
{"digest":"aa0957eca50aeb28bcd2e6930b95809926edacb550c8c340ba526ba6b861b3d8","names":[],"sources":[]},
@@ -509,8 +509,10 @@
{"digest":"b222069eea2d768065161c1147b1f2c78c1b54328c84b3586ae5c8f91b8ed35e","names":[],"sources":[]},
{"digest":"b2245a906e13aa94f2d3cc6cf7b04cfa0267f7b7f4d468de1b00b1a314e54626","names":[],"sources":[]},
{"digest":"b2a5c8c008a31e3dc6c3013502f2356aca13894b31c8e70601fa92f2d40d1dba","names":[],"sources":[]},
{"digest":"b2a948a5340a9341fa3bab99507c7090c5b0456de72ec28b5b157b8df3f1d640","names":[],"sources":["packages/core/agent/src/types.ts:110"]},
{"digest":"b2d2a01e51341a3bfec809198952c8c830a7899f4cb6ecd3c1dc0b5d9a2af943","names":["packages/experimental/agent-team/src/types.ts#TeamTaskStatus"],"sources":["packages/experimental/agent-team/src/types.ts:72"]},
{"digest":"b3681c026a0b98e98b103f057dfd217fbe904129c090efd668253ec9aea7fece","names":[],"sources":[]},
{"digest":"b36fd842cd004b532ce96fd5d6f5be28e017eda043131aa3cf5e69edc436108e","names":[],"sources":[]},
{"digest":"b38f206d59bad891fa58c89f97a0036987b3defe7685acbd318fa55eb85f9751","names":[],"sources":[]},
{"digest":"b4ec0af39fd52ed971990e42f04e5be3204882e555c409eb330f8238eec21569","names":[],"sources":[]},
{"digest":"b5086d249e8502e9ead1d39156bb8d559bde7951cac0f14ce150345b4e42a2bf","names":[],"sources":[]},
@@ -519,12 +521,14 @@
{"digest":"b56f6a885da3dc394c69a3e3cc5c0cfc4da15601b63fda4989eee8f40efc5be4","names":["packages/session/session-title/src/types.ts#SessionTitleEventData"],"sources":["packages/session/session-title/src/types.ts:41"]},
{"digest":"b57a2889408de36e4516807194c73bdecd51a92f633ccd05db81bf049200f420","names":[],"sources":[]},
{"digest":"b57ae1b37e52bee0a21cbdace168dc6a15582bcda8c306dad962c5dcc8a8e28b","names":[],"sources":[]},
{"digest":"b613f83572fe2fbf7259c72308cafd008f4fceea897749128a48bcd158f290aa","names":[],"sources":[]},
{"digest":"b63b089a00b3b9357675b3e0c3d3e9aa4fdbda1fba9c91a6b0c59ffdbb89f505","names":["packages/schedule/schedule/src/types.ts#LegacyAtScheduleRecord"],"sources":["packages/schedule/schedule/src/types.ts:167"]},
{"digest":"b6896938660a1dfe4ddd100fb3164d9f8fa57feeabfe37441a80106c9942697a","names":[],"sources":[]},
{"digest":"b6ef9c6210be8350cd465f485cf520031fffa6bf3809c61a1e713e834c5982d0","names":[],"sources":["packages/core/session/src/types.ts:203"]},
{"digest":"b6f94c17dd9bbd7cd57015198b7c7c88407f0e3b89faab176ca3c11c3d864d3e","names":[],"sources":[]},
{"digest":"b70f3e81fe11b7a5f2504fb47a4502fb8d9c8f0731fdfe8ddcaf9b41f5a6e196","names":[],"sources":[]},
{"digest":"b765ee31033cea463863faf3fc6a0a1b85c47c6a2f13fe1ce833abe56105114b","names":[],"sources":[]},
{"digest":"b782058cef83caf3eb44a16adeef5657e4d5cdff2d50655aba418c4490670a69","names":["packages/llm/llm/src/message.ts#UserMessage"],"sources":["packages/llm/llm/src/message.ts:162"]},
{"digest":"b79641a9d25e756636dc0c706fdf3c4583ccb7d4920e009e6e86c0759cf4de4c","names":[],"sources":[]},
{"digest":"b79ada42962cad0190a9d465805260567621fa3a4abd757eb31e6016b52d5ab5","names":[],"sources":[]},
{"digest":"b815d7b91cdd9fc6ccc7dcba05cfc5ce1d55fab5fc0c0801803cd3630cedb602","names":[],"sources":[]},
@@ -534,6 +538,7 @@
{"digest":"b9305f0ea75ccd6a6061c27fde19f2ab85534f9f3c1a717b573b077ebae0f611","names":[],"sources":[]},
{"digest":"b978cff734e62143eb56c9125423ec275405eda969802d42aaf73ecb987d3b26","names":[],"sources":[]},
{"digest":"b993441f8ce7d27b80e619e113e232ae8e62d3d5a8340f6bfe12d8c4c018e62f","names":[],"sources":[]},
{"digest":"ba2a1a8f194b1bb21f270713398f6643e206e8ff5f7f26f6d36a88e3f0f0ab10","names":[],"sources":[]},
{"digest":"ba4c40b4381e0dcdfab8fc9fc75b5c9b3e8f09ef56deeb1bec8e703b0ef8cd6d","names":[],"sources":[]},
{"digest":"ba77b58995e36f14bb13886b0493d0a1a63d5db10b1a6cd9f6ac65a72cf832ec","names":[],"sources":[]},
{"digest":"bab68657940075483f086fffb711682a45bcc99c43d659fbe1c36af21ac05dd7","names":[],"sources":[]},
@@ -568,6 +573,7 @@
{"digest":"cbca49bde16b656d5b8fbe4a8a507769f7b594c6287782b809c03814941bc5e3","names":[],"sources":[]},
{"digest":"cd186c309e2bef305705e1d324139240f5e681f4f6a957d6a25b0225f65560df","names":[],"sources":[]},
{"digest":"cda2345a9be9d139df49ccbd16dcba716f3440178bf20ff8b801fda1f5eb77dc","names":[],"sources":[]},
{"digest":"cda79b839d8cc0c7a7bd09dd067de04925f6e9037080d878b65aaa5a383f117c","names":["packages/llm/llm/src/message.ts#Message"],"sources":["packages/llm/llm/src/message.ts:196"]},
{"digest":"cdca80b8dad24d4e7b1d5a288a8475ba8d8d46be385bd2da85f122ee836f1670","names":[],"sources":[]},
{"digest":"cdee10e87dbed8440d8b6935888624884878efc7af95402e94171b29f173dced","names":[],"sources":["packages/llm/llm-retry/src/types.ts:17"]},
{"digest":"cef171adf021a1867d244618a3dbbaafc22beb17c7f3df3ee203393271c8c6f6","names":["packages/core/tool-executor-ptc/src/types.ts#PtcDispatchEventData"],"sources":["packages/core/tool-executor-ptc/src/types.ts:21"]},
@@ -592,9 +598,11 @@
{"digest":"db874d463b0fdec77e9da1c4568f37cb70bd6596781eb93800db44fb8a116965","names":[],"sources":[]},
{"digest":"dba54c09c6e7aafc9acbed7792ecc920d99f92d0784aeeed10af55f7fe0bc8cd","names":[],"sources":["packages/core/tool-executor-ptc/src/types.ts:68"]},
{"digest":"dbdafceb9a900183cccc80596498d386f3bf7bddead2b8cfde68453aa4739edc","names":[],"sources":[]},
{"digest":"dd00cdccd0ba487b92362ef67db58cd264f48c1bfd368291424a1da9a899489a","names":[],"sources":[]},
{"digest":"dd2780e7e07684332f7a9ab047237798d260e7eaffb1321cb38eca695d184c3d","names":[],"sources":[]},
{"digest":"dd5ee8762bd8a14b6c6bffedd1753e4e78505bb0f9ea1621018d03a54eef59d5","names":[],"sources":[]},
{"digest":"dd6cc2bb2a8f28248347021ad93565360b196a042360b4cd16b569d3a2a91f2d","names":[],"sources":[]},
{"digest":"dde0cdf9d426768b649b5a9783d2144e7f900c35ef65229cd89036dbf411d1b0","names":[],"sources":[]},
{"digest":"de25352d2dc4196309bd84b5bac6319f6d5c898ea0e82bb5ae66c5738d0d426f","names":[],"sources":[]},
{"digest":"de30b1fa12c75f81df6aa4096e1595296d2d04e2be14a7c29849cf8028b79048","names":[],"sources":[]},
{"digest":"deaae5cd30987d4f49398e8f9d994c125907e7f928300745fb1dbf15f145d0e7","names":[],"sources":[]},
@@ -624,6 +632,7 @@
{"digest":"e7ce49f12953583fad217bf80fb9f1463acff2c3e9471a823782661675fb124c","names":[],"sources":[]},
{"digest":"e7e154d47d5fbc5366330fb4dade1f108c6dfe4335044d21ded14b7bf1dce084","names":[],"sources":[]},
{"digest":"e7e8f1318170b8284f16ab45a557afd4675c96071c0f2a456636481dab20b427","names":[],"sources":[]},
{"digest":"e7f4cbb3a57f4cbdb61ea41e95bcba626ad1fff6bb9b1e063b1d50a32da6e864","names":[],"sources":[]},
{"digest":"e8de0f457fed5b9ff94dc4950b59ef75fd8ba8af665cc7899485924a62653367","names":[],"sources":[]},
{"digest":"e95f957154b2b1b58692af53d4131180b6dd19e2264869f876dbaa959991494e","names":[],"sources":[]},
{"digest":"ea08ec1df0c33108fd03345b9af199cf04dea450aa045d3c683f216311403403","names":[],"sources":["packages/experimental/agent-team/src/types.ts:234"]},
@@ -639,6 +648,7 @@
{"digest":"ef11a3b7fa2033ae4279545ff8a8a2ee4e301b9b73e5b91654c48e6d08283545","names":[],"sources":["packages/core/session/src/types.ts:301","packages/core/session/src/types.ts:303"]},
{"digest":"ef226a90ddac6cd8e631a3a337a3a3e27f9494be92befb7c718b404681d75e21","names":[],"sources":[]},
{"digest":"ef4f00923b9edbae15c7ef05320280bf61147ca17146cf59690359bfd5e5da45","names":[],"sources":[]},
{"digest":"efa1a091017ce533e38b0ef3e14d11a5c102ff1793229b18dad719a4268d9adc","names":[],"sources":[]},
{"digest":"f05fb85e715af4ab4de0b6ba111166dd0435bd8bb74a0fa0f32b9d174113fc7c","names":[],"sources":[]},
{"digest":"f09e5e7acf5ff5ccd193ff2eeec015fa2ff7c29d98bc465f8c3302058d47e29b","names":["packages/core/agent/src/types.ts#ModelSelection"],"sources":["packages/core/agent/src/types.ts:79"]},
{"digest":"f1687f982fa3d41ef29e159feed154ecd13af4dd209acaee16038f223f2e9402","names":["packages/core/session/src/types.ts#TurnEndReason"],"sources":["packages/core/session/src/types.ts:234"]},
@@ -650,7 +660,6 @@
{"digest":"f2da0f27c1e6add59982c37b15e4a4912c5b33466859cce3b13d503600e24aef","names":["packages/llm/llm/src/types.ts#FileBlock"],"sources":["packages/llm/llm/src/types.ts:98"]},
{"digest":"f311671e07bc7fa645a0b6597de9966b97147956565296ba76a36543801b66c5","names":["packages/llm/llm/src/types.ts#SystemPromptUpdate"],"sources":["packages/llm/llm/src/types.ts:414"]},
{"digest":"f4a66f654996c093aae700ea4c434ff2ee3569fea9c0e5813ccf6afdccabd163","names":[],"sources":[]},
{"digest":"f4ed4717b4c259e933b550a8dcd90dc93628cda8a3c7affcaa53b9c13c1d53eb","names":[],"sources":["packages/core/session/src/types.ts:313"]},
{"digest":"f5520c1d261ca86ea03735799c311a0a9ede8babb588cf0a65422576d2ee0ad1","names":[],"sources":[]},
{"digest":"f56c5b59ca0161310c338d31dd16cf14287ccd602ff4181058df178c1c286d7f","names":["packages/context/session-reference/src/types.ts#SessionReferenceSource"],"sources":["packages/context/session-reference/src/types.ts:13"]},
{"digest":"f6329fb30c7ea8013e8975ca5e78284fb7685cc6bd620e8aa4e9a99bc81d45eb","names":[],"sources":[]},
@@ -666,7 +675,6 @@
{"digest":"f90eb4ab2d3897bc20c521a038e3119d581f791ec974b1a0004f63a8ab9e232d","names":[],"sources":["packages/core/session/src/types.ts:290","packages/deliverables/workspace-changes/src/types.ts:106"]},
{"digest":"f97f47b0eb87905ed77cc27c5e4ed75ef805cb372c98905253709de1aab9b63c","names":[],"sources":[]},
{"digest":"f9cfc89e8fe085b4fcc9cd32b74b6f103608c54d7d8cd97b47c01de34f252ba3","names":[],"sources":[]},
{"digest":"fa106c5b33b657e8ef16e8cbf39f3b7d44e5cbcdf4f9303d9db6410f28951e42","names":[],"sources":["packages/core/agent/src/types.ts:110"]},
{"digest":"fa87537f4a9c1e25602990a8bf686b383e9e00352ec8946e16ae24ba7c1625be","names":["packages/llm/llm/src/message.ts#AssistantMessage"],"sources":["packages/llm/llm/src/message.ts:167"]},
{"digest":"fab2bb09bc5e3b9db0685e8f5500b029af1a2b33e70bc703c1d5d0f596fbc1fd","names":["packages/sandbox/sandbox/src/index.ts#SandboxMode"],"sources":["packages/sandbox/sandbox/src/index.ts:30"]},
{"digest":"fab35c4520883c0d69e42d079c9f543fc5ac7a2d15b537b5572fcd99059c7018","names":[],"sources":[]},
@@ -684,18 +692,16 @@
{"digest":"fee1caf8ab6a65c1bc981f5a1730164490f22a2a89ffa3d0b76f1f432c44c8b5","names":[],"sources":[]}
],
"nodes": {
"00c054d3018c198d": {"kind":"object","properties":[{"name":"maxTokens","type":"0657d3ca5ac4e7bf","optional":false},{"name":"messageSeqs","type":"5d03ba38734809bc","optional":false},{"name":"messages","type":"a927d444c928ac0e","optional":false},{"name":"reasoningEffort","type":"4cd48f3e5108bd6e","optional":true},{"name":"route","type":"07e1a58c58b593f5","optional":false},{"name":"system","type":"4cd48f3e5108bd6e","optional":false},{"name":"titleProvider","type":"4cd48f3e5108bd6e","optional":false}],"indices":[]},
"0116b1f89c05221d": {"kind":"literal","value":"web_search_20250305"},
"0203243b72fc867a": {"kind":"object","properties":[{"name":"kind","type":"24b10f7cdae64928","optional":false}],"indices":[]},
"022623b9ef98ec47": {"kind":"object","properties":[{"name":"form","type":"383cf73ae4748e5b","optional":false},{"name":"kind","type":"f68c335306535a00","optional":false}],"indices":[]},
"023a355b2be0d688": {"kind":"object","properties":[{"name":"kind","type":"254dce209e735f9c","optional":false}],"indices":[]},
"02b2c23f2d94b028": {"kind":"object","properties":[{"name":"kind","type":"c5687b1313206a6f","optional":false}],"indices":[]},
"0341d4c3dfcd5325": {"kind":"literal","value":"product-interaction"},
"040cd65423bd6426": {"kind":"union","types":["09f3f8dc417f2782","0f6265188dae7ba6","2cf075b013d363ad","4b259f7f8f0a44ad","fa87537f4a9c1e25"]},
"05205074b24d8340": {"kind":"literal","value":"action-cards"},
"05a2796621b1ee6c": {"kind":"literal","value":"unknown"},
"05d2ae1e0c7f8770": {"kind":"union","types":["1571525adc95a24e","9e07693fd7cd9bab"]},
"0657d3ca5ac4e7bf": {"kind":"primitive","type":"number"},
"0755b7d1128319ba": {"kind":"object","properties":[{"name":"data","type":"fa106c5b33b657e8","optional":false},{"name":"ignorable","type":"dacf3d5f3e4ff619","optional":true},{"name":"seq","type":"0657d3ca5ac4e7bf","optional":false},{"name":"time","type":"0657d3ca5ac4e7bf","optional":false},{"name":"type","type":"829a2fb3be6efd4b","optional":false}],"indices":[]},
"07e1a58c58b593f5": {"kind":"object","properties":[{"name":"model","type":"4cd48f3e5108bd6e","optional":false},{"name":"provider","type":"4cd48f3e5108bd6e","optional":false}],"indices":[]},
"07eccc9ea2f9b543": {"kind":"literal","value":"security-privacy-permission"},
"09655b34d7f3cf1e": {"kind":"literal","value":"system-prompt"},
@@ -717,7 +723,6 @@
"0ed8d1108fe7a5e1": {"kind":"object","properties":[{"name":"kind","type":"a9059a81430b3207","optional":false}],"indices":[]},
"0eeb17e1df89b1eb": {"kind":"object","properties":[{"name":"form","type":"fee1caf8ab6a65c1","optional":false},{"name":"kind","type":"24b10f7cdae64928","optional":false}],"indices":[]},
"0f46153645c29784": {"kind":"object","properties":[{"name":"height","type":"0657d3ca5ac4e7bf","optional":false},{"name":"width","type":"0657d3ca5ac4e7bf","optional":false}],"indices":[]},
"0f6265188dae7ba6": {"kind":"object","properties":[{"name":"content","type":"b52f215a6cd69105","optional":false},{"name":"id","type":"4cd48f3e5108bd6e","optional":false},{"name":"role","type":"589d0d91239681f8","optional":false},{"name":"source","type":"5bc2d7f02503fc6b","optional":false,"compatibility":{"version":1,"policy":"session-source-attribution","binding":"session.developer-message.source","discriminator":"kind","unknownKinds":"preserve","attributionKinds":["tmux-context","user-question-reply","working-directory"]}}],"indices":[]},
"0f8512903d94f57a": {"kind":"object","properties":[{"name":"data","type":"e481c46af3813e00","optional":false},{"name":"ignorable","type":"dacf3d5f3e4ff619","optional":true},{"name":"seq","type":"0657d3ca5ac4e7bf","optional":false},{"name":"time","type":"0657d3ca5ac4e7bf","optional":false},{"name":"type","type":"4312134fc6f9cad5","optional":false}],"indices":[]},
"0fe52387f6505270": {"kind":"literal","value":"plan-mode"},
"1033093edd0db80f": {"kind":"object","properties":[{"name":"data","type":"1ccb56061942f5cc","optional":false},{"name":"ignorable","type":"dacf3d5f3e4ff619","optional":true},{"name":"seq","type":"0657d3ca5ac4e7bf","optional":false},{"name":"surfaceOp","type":"335e242de1fcc17b","optional":false},{"name":"time","type":"0657d3ca5ac4e7bf","optional":false},{"name":"type","type":"6ab45ac76839722d","optional":false}],"indices":[]},
@@ -794,11 +799,9 @@
"2aad1179f9576314": {"kind":"object","properties":[{"name":"form","type":"6f9e00383836275e","optional":false},{"name":"kind","type":"24b10f7cdae64928","optional":false},{"name":"sections","type":"c748fb42c80a1200","optional":false}],"indices":[]},
"2ab12a0e345658f9": {"kind":"array","element":"83da72c6857601c7"},
"2abb711460e6238d": {"kind":"object","properties":[{"name":"index","type":"0657d3ca5ac4e7bf","optional":false},{"name":"text","type":"4cd48f3e5108bd6e","optional":false},{"name":"type","type":"6f091fe32ab24407","optional":false}],"indices":[]},
"2ae8594a998c9862": {"kind":"object","properties":[{"name":"data","type":"2cf075b013d363ad","optional":false},{"name":"ignorable","type":"dacf3d5f3e4ff619","optional":true},{"name":"seq","type":"0657d3ca5ac4e7bf","optional":false},{"name":"sourceEventSeqs","type":"5d03ba38734809bc","optional":true},{"name":"surfaceOp","type":"335e242de1fcc17b","optional":false},{"name":"time","type":"0657d3ca5ac4e7bf","optional":false},{"name":"type","type":"de25352d2dc41963","optional":false}],"indices":[]},
"2aed07493b1c65b6": {"kind":"literal","value":"tool/ptc-dispatch-start"},
"2c4c21f19f426a58": {"kind":"literal","value":"unavailable"},
"2c64d4bc7264fd3f": {"kind":"literal","value":"step/start"},
"2cf075b013d363ad": {"kind":"object","properties":[{"name":"content","type":"b52f215a6cd69105","optional":false},{"name":"id","type":"4cd48f3e5108bd6e","optional":false},{"name":"role","type":"68f44f59e91c4139","optional":false},{"name":"source","type":"5bc2d7f02503fc6b","optional":false,"compatibility":{"version":1,"policy":"session-source-attribution","binding":"session.user-message.source","discriminator":"kind","unknownKinds":"preserve","attributionKinds":["tmux-context","user-question-reply","working-directory"]}}],"indices":[]},
"2d11ca7b0d4493e2": {"kind":"object","properties":[{"name":"max_uses","type":"0657d3ca5ac4e7bf","optional":false},{"name":"name","type":"935c8d486292b13c","optional":false},{"name":"type","type":"0116b1f89c05221d","optional":false}],"indices":[]},
"2da725e554cd8930": {"kind":"literal","value":"fork"},
"2e3523541e942ef6": {"kind":"union","types":["18dc21f12bbac8ad","8e8e91646a5cae3f","e6063d0451dc0202","eea687d437880bd5"]},
@@ -810,6 +813,7 @@
"30d5a537b2966857": {"kind":"union","types":["1b5649238876e288","ada310bf0bdb8fed","b81e9de81a481b24"]},
"312aee2f0c42aabb": {"kind":"object","properties":[{"name":"form","type":"e8de0f457fed5b9f","optional":false},{"name":"kind","type":"d9edf1862e4471d6","optional":false},{"name":"summary","type":"4cd48f3e5108bd6e","optional":false}],"indices":[]},
"31907de956375182": {"kind":"object","properties":[{"name":"compactionId","type":"4cd48f3e5108bd6e","optional":false},{"name":"kind","type":"6b23ed9ce1b397e6","optional":false},{"name":"sourceCommandId","type":"4cd48f3e5108bd6e","optional":true}],"indices":[]},
"3247e566f4340029": {"kind":"object","properties":[{"name":"form","type":"6f9e00383836275e","optional":false},{"name":"kind","type":"05205074b24d8340","optional":false},{"name":"sections","type":"c748fb42c80a1200","optional":false},{"name":"through","type":"0657d3ca5ac4e7bf","optional":false}],"indices":[]},
"326a9bfda27dda58": {"kind":"object","properties":[{"name":"maxTokens","type":"dacf3d5f3e4ff619","optional":true},{"name":"reasoningEffort","type":"dacf3d5f3e4ff619","optional":true}],"indices":[]},
"3305a4bb6b6c29b4": {"kind":"union","types":["0341d4c3dfcd5325","07eccc9ea2f9b543","5bd310104cfe448e","7180844e1066230c","9d80129e13edfb6f","e4cac694f8b6e1d5","f05fb85e715af4ab"]},
"33341aac09312300": {"kind":"union","types":["3860b4757d512a28","487b65c37bfedb8b","71bb3254ec8f1e49","e72269f931559e90"]},
@@ -852,7 +856,7 @@
"44818000e56acb97": {"kind":"literal","value":"feedback/message-delete"},
"44c8c77aa9aa100b": {"kind":"literal","value":"subagent/catalog"},
"4513e088d43e6c68": {"kind":"object","properties":[{"name":"data","type":"ef11a3b7fa2033ae","optional":false},{"name":"ignorable","type":"dacf3d5f3e4ff619","optional":true},{"name":"seq","type":"0657d3ca5ac4e7bf","optional":false},{"name":"time","type":"0657d3ca5ac4e7bf","optional":false},{"name":"type","type":"2c64d4bc7264fd3f","optional":false}],"indices":[]},
"454a0fa879e38076": {"kind":"array","element":"2cf075b013d363ad"},
"45f005e6bbb76452": {"kind":"object","properties":[{"name":"form","type":"16d0507cb94b4cfc","optional":false},{"name":"kind","type":"05205074b24d8340","optional":false},{"name":"through","type":"0657d3ca5ac4e7bf","optional":false}],"indices":[]},
"45f2a07ea9457312": {"kind":"object","properties":[{"name":"callId","type":"4cd48f3e5108bd6e","optional":true},{"name":"id","type":"4cd48f3e5108bd6e","optional":false},{"name":"reason","type":"4cd48f3e5108bd6e","optional":true},{"name":"toolName","type":"4cd48f3e5108bd6e","optional":false}],"indices":[]},
"477614bca595a9e7": {"kind":"object","properties":[{"name":"targets","type":"9e2c06d8653585b3","optional":false}],"indices":[]},
"47e5d84f1a93450a": {"kind":"literal","value":"session-log-deepseek/delivery-accepted"},
@@ -901,17 +905,18 @@
"5808e36c95355f0e": {"kind":"union","types":["1d76406cbb2ddadb","287211846beea1ec","693f4952fb5565da","8e31bb958e9b015e","ab7ec5f9eb2c6dfa"]},
"587bb11da730e06a": {"kind":"literal","value":"session/title-llm-request"},
"589d0d91239681f8": {"kind":"literal","value":"developer"},
"58b122df513aeaf7": {"kind":"object","properties":[{"name":"data","type":"b782058cef83caf3","optional":false},{"name":"ignorable","type":"dacf3d5f3e4ff619","optional":true},{"name":"seq","type":"0657d3ca5ac4e7bf","optional":false},{"name":"sourceEventSeqs","type":"5d03ba38734809bc","optional":true},{"name":"surfaceOp","type":"335e242de1fcc17b","optional":false},{"name":"time","type":"0657d3ca5ac4e7bf","optional":false},{"name":"type","type":"de25352d2dc41963","optional":false}],"indices":[]},
"58f94390b982f8a5": {"kind":"literal","value":"image/png"},
"5abab3a27fd27366": {"kind":"literal","value":"text-delta"},
"5b232a9b9bfcc0fc": {"kind":"literal","value":"after"},
"5b6fb1f226ff5640": {"kind":"object","properties":[{"name":"commandId","type":"4cd48f3e5108bd6e","optional":false},{"name":"kind","type":"1ab2ef1296f157ba","optional":false},{"name":"sourceEventSeq","type":"0657d3ca5ac4e7bf","optional":true},{"name":"text","type":"4cd48f3e5108bd6e","optional":true}],"indices":[]},
"5bb69b88517f6214": {"kind":"object","properties":[{"name":"form","type":"e8de0f457fed5b9f","optional":false},{"name":"kind","type":"99e7e7d7fed55ae6","optional":false},{"name":"summary","type":"4cd48f3e5108bd6e","optional":false}],"indices":[]},
"5bc2d7f02503fc6b": {"kind":"union","types":["0203243b72fc867a","022623b9ef98ec47","02b2c23f2d94b028","0ed8d1108fe7a5e1","0eeb17e1df89b1eb","11638dc58a31496f","1938c8c20f1cba7c","1e709346005826eb","1fdad624334d3548","2152caf1a71e588e","2235d957bf4b7c6c","224aa11d4d5f88c4","24ce31aa528272e5","260523b412c19ad8","28936c614bd4e7e2","290f32bfd81ff85a","297fbd8067d7815a","2aad1179f9576314","312aee2f0c42aabb","31907de956375182","3b04790e08b35241","3b4f2c9ef2c1217e","3bdb8dc6989655b0","408927267422a7b2","4991dfc415c9efeb","49dab019d00e8c26","4a9bef9f27a94252","4e97c3d85c0fc817","52c6113cb0e53f34","53cf1704ae0709c7","5654685d7557ec74","58015da1c1950a66","5bb69b88517f6214","5bc66269cfaf614c","5cdbf342b7748d15","5d7a64ed6aac5551","6085488d223aeffd","6292bcbedaa97fea","65a8400e451e667e","672c4aa2a9b736e3","6ccb6bc423fb3952","6dacb1584f2bfbe2","6ea608bdc8a7a63d","6f22a9d02b9a1f11","6f44f32be9bcd9a8","719c51f67074f970","7554565be416b034","756b5e23a5cb2c7f","7df62978a2b7de85","7eaef7a763ba5b94","7f959241c24128b4","80677c8c8b804189","825ca23416b1ea9d","8338ee303e2bd448","854db3efdfb0cd39","85fe96bf15d5c755","8620189c8e335827","87b1a33564b9a4d6","88841202942c7683","8a046b52331959aa","8c1c4f72755a78ea","8c627d7a45fa406c","8f324cea3916c051","8fcb0b44a575e998","96bfb916a727cd44","99135d8a39379779","99f0e98184709ad1","9edc162949abf93b","9f65c43e340bba31","a15a19ef63cf5d1b","a16e567c8cd2c55c","a2066bd52f61218b","a3c6f1ea38e9a61d","aa2bf9e82b9fdeb9","aae343bc3d663c83","ada310bf0bdb8fed","ade057f4fdc387ae","b062078e899958f9","b2a5c8c008a31e3d","b3681c026a0b98e9","b4ec0af39fd52ed9","b6f94c17dd9bbd7c","b70f3e81fe11b7a5","b79641a9d25e7566","ba4c40b4381e0dcd","baccc97ec34be6c4","c5228b7a55498d84","c55827fba7018198","cb1c8959e1726f82","cda2345a9be9d139","d85590a5675c6e2f","d9c462b9f62fdd3e","db06ef35b27a3842","dba54c09c6e7aafc","dd5ee8762bd8a14b","dd6cc2bb2a8f2824","deaae5cd30987d4f","edbbfff1128099fd","ee577a0cc18664c9","f230c0b583e23b25","f29d63336e8266d8","f4a66f654996c093","f56c5b59ca016131","f6329fb30c7ea801","f6605d6c57933aea","f6afe41e3926f422","f7283223aacdc39b","f97f47b0eb87905e","fc2ae6962f9f8801","fcf81d3c58c13593","fe1a7acdeed4d0f5"]},
"5bc66269cfaf614c": {"kind":"object","properties":[{"name":"form","type":"16d0507cb94b4cfc","optional":false},{"name":"kind","type":"d9edf1862e4471d6","optional":false}],"indices":[]},
"5bd310104cfe448e": {"kind":"literal","value":"resource-cost"},
"5c45bf4c544a7211": {"kind":"object","properties":[{"name":"data","type":"8a9c990773f798ea","optional":false},{"name":"ignorable","type":"dacf3d5f3e4ff619","optional":true},{"name":"seq","type":"0657d3ca5ac4e7bf","optional":false},{"name":"time","type":"0657d3ca5ac4e7bf","optional":false},{"name":"type","type":"ae63d9d1080ed894","optional":false}],"indices":[]},
"5cb63db9fe57dc09": {"kind":"literal","value":"teardown"},
"5cdbf342b7748d15": {"kind":"object","properties":[{"name":"form","type":"16d0507cb94b4cfc","optional":false},{"name":"kind","type":"f68c335306535a00","optional":false}],"indices":[]},
"5d00abf938272f22": {"kind":"array","element":"b782058cef83caf3"},
"5d03ba38734809bc": {"kind":"array","element":"0657d3ca5ac4e7bf"},
"5d17f27a1ef9f702": {"kind":"literal","value":"tool-workflow/agent-end"},
"5d542ac7bdf13310": {"kind":"object","properties":[{"name":"childCreatedAt","type":"0657d3ca5ac4e7bf","optional":false},{"name":"childId","type":"4cd48f3e5108bd6e","optional":false},{"name":"label","type":"4cd48f3e5108bd6e","optional":true},{"name":"mode","type":"d28765f87d6b6ab4","optional":false},{"name":"version","type":"93d38436347f67c9","optional":false}],"indices":[]},
@@ -919,6 +924,7 @@
"5dd766ccd1702a11": {"kind":"object","properties":[{"name":"shadowedRange","type":"9e41386b3a0c9572","optional":false},{"name":"shadowedSeqs","type":"5d03ba38734809bc","optional":false},{"name":"shadowedTokenCount","type":"0657d3ca5ac4e7bf","optional":false}],"indices":[]},
"5e6db6e24948d4a8": {"kind":"object","properties":[{"name":"data","type":"17d1afb770d99419","optional":false},{"name":"ignorable","type":"dacf3d5f3e4ff619","optional":true},{"name":"seq","type":"0657d3ca5ac4e7bf","optional":false},{"name":"time","type":"0657d3ca5ac4e7bf","optional":false},{"name":"type","type":"1c2686c0682e7bc5","optional":false}],"indices":[]},
"5ebce1317eacd2c1": {"kind":"literal","value":"subagent/descriptor"},
"5ef58f2a1bdc576c": {"kind":"object","properties":[{"name":"data","type":"7404c6123fdaa591","optional":false},{"name":"ignorable","type":"dacf3d5f3e4ff619","optional":true},{"name":"seq","type":"0657d3ca5ac4e7bf","optional":false},{"name":"sourceEventSeqs","type":"5d03ba38734809bc","optional":true},{"name":"surfaceOp","type":"335e242de1fcc17b","optional":false},{"name":"time","type":"0657d3ca5ac4e7bf","optional":false},{"name":"type","type":"1379f0b72275994d","optional":false}],"indices":[]},
"5f2117d1856a5ab4": {"kind":"object","properties":[{"name":"code","type":"4cd48f3e5108bd6e","optional":false},{"name":"message","type":"4cd48f3e5108bd6e","optional":false},{"name":"offloadImages","type":"0657d3ca5ac4e7bf","optional":true},{"name":"providerRetryAfterMs","type":"0657d3ca5ac4e7bf","optional":true},{"name":"requestId","type":"4cd48f3e5108bd6e","optional":true},{"name":"status","type":"0657d3ca5ac4e7bf","optional":true}],"indices":[]},
"5f26a6c20b37632f": {"kind":"object","properties":[{"name":"data","type":"9a410ddf56792b2b","optional":false},{"name":"ignorable","type":"dacf3d5f3e4ff619","optional":true},{"name":"seq","type":"0657d3ca5ac4e7bf","optional":false},{"name":"time","type":"0657d3ca5ac4e7bf","optional":false},{"name":"type","type":"ad92f473a03ae725","optional":false}],"indices":[]},
"5f758f3de5f00c18": {"kind":"object","properties":[{"name":"label","type":"4cd48f3e5108bd6e","optional":true},{"name":"mode","type":"d28765f87d6b6ab4","optional":false},{"name":"provider","type":"4cd48f3e5108bd6e","optional":false},{"name":"version","type":"0657d3ca5ac4e7bf","optional":false}],"indices":[]},
@@ -967,16 +973,19 @@
"71bb3254ec8f1e49": {"kind":"object","properties":[{"name":"dt","type":"5d03ba38734809bc","optional":false},{"name":"index","type":"0657d3ca5ac4e7bf","optional":false},{"name":"texts","type":"93c33d9687613293","optional":false},{"name":"time0","type":"0657d3ca5ac4e7bf","optional":false},{"name":"type","type":"688e853dc149f0d3","optional":false}],"indices":[]},
"72ec79127a9c0d02": {"kind":"array","element":"c592ce75aab73fca"},
"7382c4fc95fe8c5d": {"kind":"literal","value":"remove"},
"7404c6123fdaa591": {"kind":"object","properties":[{"name":"headerSeq","type":"0657d3ca5ac4e7bf","optional":true},{"name":"message","type":"7580e7d96dc8475f","optional":false},{"name":"step","type":"0657d3ca5ac4e7bf","optional":false},{"name":"turn","type":"0657d3ca5ac4e7bf","optional":false}],"indices":[]},
"750a2b129d67704d": {"kind":"union","types":["5cb63db9fe57dc09","6cf082d0fb6034de","bab6865794007548"]},
"7554565be416b034": {"kind":"object","properties":[{"name":"form","type":"383cf73ae4748e5b","optional":false},{"name":"kind","type":"2f3f201c8846e19c","optional":false}],"indices":[]},
"756b5e23a5cb2c7f": {"kind":"object","properties":[{"name":"form","type":"383cf73ae4748e5b","optional":false},{"name":"kind","type":"8067b0d232c350f1","optional":false}],"indices":[]},
"7580e7d96dc8475f": {"kind":"object","properties":[{"name":"content","type":"b52f215a6cd69105","optional":false},{"name":"id","type":"4cd48f3e5108bd6e","optional":false},{"name":"role","type":"589d0d91239681f8","optional":false},{"name":"source","type":"7a5b68bd1a5118c8","optional":false,"compatibility":{"version":1,"policy":"session-source-attribution","binding":"session.developer-message.source","discriminator":"kind","unknownKinds":"preserve","attributionKinds":["action-cards","tmux-context","user-question-reply","working-directory"]}}],"indices":[]},
"7624ca28c61ca0ea": {"kind":"literal","value":"create"},
"763c8a20af487263": {"kind":"object","properties":[{"name":"data","type":"7d226b9fd95b4ba2","optional":false},{"name":"ignorable","type":"dacf3d5f3e4ff619","optional":true},{"name":"seq","type":"0657d3ca5ac4e7bf","optional":false},{"name":"time","type":"0657d3ca5ac4e7bf","optional":false},{"name":"type","type":"237c004c73b6b288","optional":false}],"indices":[]},
"76632533f67879dc": {"kind":"literal","value":"delegation"},
"78a9be340090b735": {"kind":"literal","value":"tmux-context"},
"78b9204976ab9faa": {"kind":"array","element":"cda79b839d8cc0c7"},
"79650cfa10aa3c52": {"kind":"object","properties":[{"name":"error","type":"5f2117d1856a5ab4","optional":false},{"name":"kind","type":"a2be0f0cb59f0d4a","optional":false}],"indices":[]},
"79a4c87f8bfb4d6d": {"kind":"object","properties":[{"name":"data","type":"00c054d3018c198d","optional":false},{"name":"ignorable","type":"dacf3d5f3e4ff619","optional":true},{"name":"seq","type":"0657d3ca5ac4e7bf","optional":false},{"name":"time","type":"0657d3ca5ac4e7bf","optional":false},{"name":"type","type":"587bb11da730e06a","optional":false}],"indices":[]},
"79ddc907312f97cf": {"kind":"object","properties":[{"name":"todos","type":"4b7ddaf3710b88ff","optional":false}],"indices":[]},
"7a5b68bd1a5118c8": {"kind":"union","types":["0203243b72fc867a","022623b9ef98ec47","02b2c23f2d94b028","0ed8d1108fe7a5e1","0eeb17e1df89b1eb","11638dc58a31496f","1938c8c20f1cba7c","1e709346005826eb","1fdad624334d3548","2152caf1a71e588e","2235d957bf4b7c6c","224aa11d4d5f88c4","24ce31aa528272e5","260523b412c19ad8","28936c614bd4e7e2","290f32bfd81ff85a","297fbd8067d7815a","2aad1179f9576314","312aee2f0c42aabb","31907de956375182","3247e566f4340029","3b04790e08b35241","3b4f2c9ef2c1217e","3bdb8dc6989655b0","408927267422a7b2","45f005e6bbb76452","4991dfc415c9efeb","49dab019d00e8c26","4a9bef9f27a94252","4e97c3d85c0fc817","52c6113cb0e53f34","53cf1704ae0709c7","5654685d7557ec74","58015da1c1950a66","5bb69b88517f6214","5bc66269cfaf614c","5cdbf342b7748d15","5d7a64ed6aac5551","6085488d223aeffd","6292bcbedaa97fea","65a8400e451e667e","672c4aa2a9b736e3","6ccb6bc423fb3952","6dacb1584f2bfbe2","6ea608bdc8a7a63d","6f22a9d02b9a1f11","6f44f32be9bcd9a8","719c51f67074f970","7554565be416b034","756b5e23a5cb2c7f","7df62978a2b7de85","7eaef7a763ba5b94","7f959241c24128b4","80677c8c8b804189","825ca23416b1ea9d","8338ee303e2bd448","854db3efdfb0cd39","85fe96bf15d5c755","8620189c8e335827","87b1a33564b9a4d6","88841202942c7683","8a046b52331959aa","8c1c4f72755a78ea","8c627d7a45fa406c","8f324cea3916c051","8fcb0b44a575e998","96bfb916a727cd44","99135d8a39379779","99f0e98184709ad1","9edc162949abf93b","9f65c43e340bba31","a15a19ef63cf5d1b","a16e567c8cd2c55c","a2066bd52f61218b","a3c6f1ea38e9a61d","aa2bf9e82b9fdeb9","aae343bc3d663c83","ada310bf0bdb8fed","ade057f4fdc387ae","b062078e899958f9","b2a5c8c008a31e3d","b3681c026a0b98e9","b4ec0af39fd52ed9","b613f83572fe2fbf","b6f94c17dd9bbd7c","b70f3e81fe11b7a5","b79641a9d25e7566","ba2a1a8f194b1bb2","ba4c40b4381e0dcd","baccc97ec34be6c4","c5228b7a55498d84","c55827fba7018198","cb1c8959e1726f82","cda2345a9be9d139","d85590a5675c6e2f","d9c462b9f62fdd3e","db06ef35b27a3842","dba54c09c6e7aafc","dd00cdccd0ba487b","dd5ee8762bd8a14b","dd6cc2bb2a8f2824","dde0cdf9d426768b","deaae5cd30987d4f","e7f4cbb3a57f4cbd","edbbfff1128099fd","ee577a0cc18664c9","f230c0b583e23b25","f29d63336e8266d8","f4a66f654996c093","f56c5b59ca016131","f6329fb30c7ea801","f6605d6c57933aea","f6afe41e3926f422","f7283223aacdc39b","f97f47b0eb87905e","fc2ae6962f9f8801","fcf81d3c58c13593","fe1a7acdeed4d0f5"]},
"7a651f3b672c52fb": {"kind":"literal","value":"session/title"},
"7af85bf70d4eafce": {"kind":"object","properties":[{"name":"ignorable","type":"dacf3d5f3e4ff619","optional":true},{"name":"seq","type":"0657d3ca5ac4e7bf","optional":false},{"name":"surfaceOp","type":"335e242de1fcc17b","optional":false},{"name":"time","type":"0657d3ca5ac4e7bf","optional":false},{"name":"type","type":"4cd48f3e5108bd6e","optional":false}],"indices":[]},
"7b1ce60193fa8b69": {"kind":"literal","value":"working-directory"},
@@ -1047,7 +1056,6 @@
"9645cf93769a6dfb": {"kind":"literal","value":"user-question-reply"},
"96bfb916a727cd44": {"kind":"object","properties":[{"name":"form","type":"383cf73ae4748e5b","optional":false},{"name":"kind","type":"24b10f7cdae64928","optional":false}],"indices":[]},
"96f4a9c81fc0f940": {"kind":"union","types":["3e2ece4967cfebe9","62ecb62dc0fe1b5e"]},
"97e0162f983c2745": {"kind":"object","properties":[{"name":"data","type":"f4ed4717b4c259e9","optional":false},{"name":"ignorable","type":"dacf3d5f3e4ff619","optional":true},{"name":"seq","type":"0657d3ca5ac4e7bf","optional":false},{"name":"sourceEventSeqs","type":"5d03ba38734809bc","optional":true},{"name":"surfaceOp","type":"335e242de1fcc17b","optional":false},{"name":"time","type":"0657d3ca5ac4e7bf","optional":false},{"name":"type","type":"1379f0b72275994d","optional":false}],"indices":[]},
"9890113e97dd3541": {"kind":"object","properties":[{"name":"mode","type":"fab2bb09bc5e3b9d","optional":false},{"name":"source","type":"76632533f67879dc","optional":true}],"indices":[]},
"99135d8a39379779": {"kind":"object","properties":[{"name":"form","type":"aec150797b655c5f","optional":false},{"name":"kind","type":"9ac52f0f84fec2eb","optional":false}],"indices":[]},
"9947aa5938fe19a8": {"kind":"union","types":["576a2de8355eb098","a99ec0993c62c058"]},
@@ -1080,6 +1088,7 @@
"a2be0f0cb59f0d4a": {"kind":"literal","value":"error"},
"a3a259a1aa5401af": {"kind":"literal","value":"hook/result"},
"a3c6f1ea38e9a61d": {"kind":"object","properties":[{"name":"form","type":"e8de0f457fed5b9f","optional":false},{"name":"kind","type":"9ac52f0f84fec2eb","optional":false},{"name":"summary","type":"4cd48f3e5108bd6e","optional":false}],"indices":[]},
"a3e299483348f27a": {"kind":"object","properties":[{"name":"maxTokens","type":"0657d3ca5ac4e7bf","optional":false},{"name":"messageSeqs","type":"5d03ba38734809bc","optional":false},{"name":"messages","type":"78b9204976ab9faa","optional":false},{"name":"reasoningEffort","type":"4cd48f3e5108bd6e","optional":true},{"name":"route","type":"07e1a58c58b593f5","optional":false},{"name":"system","type":"4cd48f3e5108bd6e","optional":false},{"name":"titleProvider","type":"4cd48f3e5108bd6e","optional":false}],"indices":[]},
"a3edd1efd1fef21b": {"kind":"literal","value":"workspace-write"},
"a40d12070f6f4a12": {"kind":"object","properties":[{"name":"task","type":"51edec100659da3a","optional":false},{"name":"teamId","type":"4cd48f3e5108bd6e","optional":false},{"name":"version","type":"a0f906856fc06f1c","optional":false}],"indices":[]},
"a6567ccb2e530606": {"kind":"object","properties":[{"name":"data","type":"0d47a8f3d847243c","optional":false},{"name":"ignorable","type":"dacf3d5f3e4ff619","optional":true},{"name":"seq","type":"0657d3ca5ac4e7bf","optional":false},{"name":"time","type":"0657d3ca5ac4e7bf","optional":false},{"name":"type","type":"0a8a062bc9c3c0d9","optional":false}],"indices":[]},
@@ -1092,7 +1101,6 @@
"a8a2b9011fc06786": {"kind":"literal","value":"team/message/delivered"},
"a9059a81430b3207": {"kind":"literal","value":"hooks-claude-code"},
"a926dc71814edd01": {"kind":"object","properties":[{"name":"everySeconds","type":"0657d3ca5ac4e7bf","optional":false},{"name":"id","type":"4cd48f3e5108bd6e","optional":false},{"name":"kind","type":"e42b0f40b5d0b628","optional":false},{"name":"prompt","type":"4cd48f3e5108bd6e","optional":false},{"name":"scheduledAt","type":"4cd48f3e5108bd6e","optional":false},{"name":"title","type":"4cd48f3e5108bd6e","optional":true}],"indices":[]},
"a927d444c928ac0e": {"kind":"array","element":"040cd65423bd6426"},
"a99ec0993c62c058": {"kind":"object","properties":[{"name":"compactionId","type":"4cd48f3e5108bd6e","optional":false},{"name":"llmStreamCall","type":"dacf3d5f3e4ff619","optional":false},{"name":"maxTokens","type":"0657d3ca5ac4e7bf","optional":true},{"name":"model","type":"4cd48f3e5108bd6e","optional":false},{"name":"provider","type":"4cd48f3e5108bd6e","optional":false},{"name":"rawOutput","type":"b52f215a6cd69105","optional":false},{"name":"shadowedRange","type":"9e41386b3a0c9572","optional":false},{"name":"shadowedSeqs","type":"5d03ba38734809bc","optional":false},{"name":"shadowedTokenCount","type":"0657d3ca5ac4e7bf","optional":false},{"name":"sourceCommandId","type":"4cd48f3e5108bd6e","optional":true},{"name":"summary","type":"b52f215a6cd69105","optional":false},{"name":"usage","type":"fec5442b9ee4122e","optional":true}],"indices":[]},
"a99f76fd149051c3": {"kind":"object","properties":[{"name":"content","type":"4cd48f3e5108bd6e","optional":false},{"name":"status","type":"fd0d91a1f9dd2efb","optional":false}],"indices":[]},
"aa0957eca50aeb28": {"kind":"object","properties":[{"name":"data","type":"f90eb4ab2d3897bc","optional":false},{"name":"ignorable","type":"dacf3d5f3e4ff619","optional":true},{"name":"seq","type":"0657d3ca5ac4e7bf","optional":false},{"name":"time","type":"0657d3ca5ac4e7bf","optional":false},{"name":"type","type":"0af3ecfb8c60d416","optional":false}],"indices":[]},
@@ -1125,8 +1133,10 @@
"b222069eea2d7680": {"kind":"object","properties":[{"name":"data","type":"477614bca595a9e7","optional":false},{"name":"ignorable","type":"dacf3d5f3e4ff619","optional":true},{"name":"seq","type":"0657d3ca5ac4e7bf","optional":false},{"name":"time","type":"0657d3ca5ac4e7bf","optional":false},{"name":"type","type":"9acbe713f3f64192","optional":false}],"indices":[]},
"b2245a906e13aa94": {"kind":"literal","value":"forked"},
"b2a5c8c008a31e3d": {"kind":"object","properties":[{"name":"form","type":"383cf73ae4748e5b","optional":false},{"name":"kind","type":"23af3759678a30de","optional":false}],"indices":[]},
"b2a948a5340a9341": {"kind":"object","properties":[{"name":"inserted","type":"5d00abf938272f22","optional":false},{"name":"outcome","type":"d4e4d575abeb4b72","optional":true},{"name":"removedCount","type":"0657d3ca5ac4e7bf","optional":true},{"name":"start","type":"0657d3ca5ac4e7bf","optional":false},{"name":"target","type":"96f4a9c81fc0f940","optional":false}],"indices":[]},
"b2d2a01e51341a3b": {"kind":"union","types":["1ebc6f9ff3aa9e3a","234142ee4f996f38","94c56aeb1564ad73","e7e8f1318170b828"]},
"b3681c026a0b98e9": {"kind":"object","properties":[{"name":"form","type":"e8de0f457fed5b9f","optional":false},{"name":"kind","type":"78a9be340090b735","optional":false},{"name":"summary","type":"4cd48f3e5108bd6e","optional":false}],"indices":[]},
"b36fd842cd004b53": {"kind":"object","properties":[{"name":"data","type":"b2a948a5340a9341","optional":false},{"name":"ignorable","type":"dacf3d5f3e4ff619","optional":true},{"name":"seq","type":"0657d3ca5ac4e7bf","optional":false},{"name":"time","type":"0657d3ca5ac4e7bf","optional":false},{"name":"type","type":"829a2fb3be6efd4b","optional":false}],"indices":[]},
"b38f206d59bad891": {"kind":"literal","value":"disposed"},
"b4ec0af39fd52ed9": {"kind":"object","properties":[{"name":"kind","type":"bb94f3217aa56c88","optional":false}],"indices":[]},
"b5086d249e8502e9": {"kind":"object","properties":[{"name":"data","type":"aa7bc2817014ca6c","optional":false},{"name":"ignorable","type":"dacf3d5f3e4ff619","optional":true},{"name":"seq","type":"0657d3ca5ac4e7bf","optional":false},{"name":"time","type":"0657d3ca5ac4e7bf","optional":false},{"name":"type","type":"6cda0d6d3b4336d8","optional":false}],"indices":[]},
@@ -1135,12 +1145,14 @@
"b56f6a885da3dc39": {"kind":"object","properties":[{"name":"messageSeqs","type":"5d03ba38734809bc","optional":false},{"name":"source","type":"30d5a537b2966857","optional":false},{"name":"title","type":"4cd48f3e5108bd6e","optional":false}],"indices":[]},
"b57a2889408de36e": {"kind":"literal","value":"failed"},
"b57ae1b37e52bee0": {"kind":"literal","value":"team/task"},
"b613f83572fe2fbf": {"kind":"object","properties":[{"name":"form","type":"383cf73ae4748e5b","optional":false},{"name":"kind","type":"05205074b24d8340","optional":false},{"name":"through","type":"0657d3ca5ac4e7bf","optional":false}],"indices":[]},
"b63b089a00b3b935": {"kind":"object","properties":[{"name":"id","type":"4cd48f3e5108bd6e","optional":false},{"name":"kind","type":"17aae616ef020b8b","optional":false},{"name":"prompt","type":"4cd48f3e5108bd6e","optional":false},{"name":"scheduledAt","type":"4cd48f3e5108bd6e","optional":false},{"name":"title","type":"4cd48f3e5108bd6e","optional":true}],"indices":[]},
"b6896938660a1dfe": {"kind":"object","properties":[{"name":"childCreatedAt","type":"0657d3ca5ac4e7bf","optional":false},{"name":"childId","type":"4cd48f3e5108bd6e","optional":false},{"name":"label","type":"4cd48f3e5108bd6e","optional":true},{"name":"mode","type":"d28765f87d6b6ab4","optional":false},{"name":"version","type":"f88f1b7b6bb733f6","optional":false}],"indices":[]},
"b6ef9c6210be8350": {"kind":"object","properties":[{"name":"kind","type":"1ebc6f9ff3aa9e3a","optional":false}],"indices":[]},
"b6f94c17dd9bbd7c": {"kind":"object","properties":[{"name":"form","type":"fee1caf8ab6a65c1","optional":false},{"name":"kind","type":"bb94f3217aa56c88","optional":false}],"indices":[]},
"b70f3e81fe11b7a5": {"kind":"object","properties":[{"name":"form","type":"fee1caf8ab6a65c1","optional":false},{"name":"kind","type":"a9059a81430b3207","optional":false}],"indices":[]},
"b765ee31033cea46": {"kind":"literal","value":"set"},
"b782058cef83caf3": {"kind":"object","properties":[{"name":"content","type":"b52f215a6cd69105","optional":false},{"name":"id","type":"4cd48f3e5108bd6e","optional":false},{"name":"role","type":"68f44f59e91c4139","optional":false},{"name":"source","type":"7a5b68bd1a5118c8","optional":false,"compatibility":{"version":1,"policy":"session-source-attribution","binding":"session.user-message.source","discriminator":"kind","unknownKinds":"preserve","attributionKinds":["action-cards","tmux-context","user-question-reply","working-directory"]}}],"indices":[]},
"b79641a9d25e7566": {"kind":"object","properties":[{"name":"kind","type":"8067b0d232c350f1","optional":false}],"indices":[]},
"b79ada42962cad01": {"kind":"object","properties":[{"name":"data","type":"86d1d0e9cfebb240","optional":false},{"name":"ignorable","type":"dacf3d5f3e4ff619","optional":true},{"name":"seq","type":"0657d3ca5ac4e7bf","optional":false},{"name":"time","type":"0657d3ca5ac4e7bf","optional":false},{"name":"type","type":"5ebce1317eacd2c1","optional":false}],"indices":[]},
"b815d7b91cdd9fc6": {"kind":"literal","value":"goal"},
@@ -1150,6 +1162,7 @@
"b9305f0ea75ccd6a": {"kind":"literal","value":"hook/invoked"},
"b978cff734e62143": {"kind":"object","properties":[{"name":"data","type":"79ddc907312f97cf","optional":false},{"name":"ignorable","type":"dacf3d5f3e4ff619","optional":true},{"name":"seq","type":"0657d3ca5ac4e7bf","optional":false},{"name":"time","type":"0657d3ca5ac4e7bf","optional":false},{"name":"type","type":"af0750d32d97d47b","optional":false}],"indices":[]},
"b993441f8ce7d27b": {"kind":"tuple","elements":[{"type":"9a2a9029f8d7ede0","optional":false,"rest":false}]},
"ba2a1a8f194b1bb2": {"kind":"object","properties":[{"name":"form","type":"fee1caf8ab6a65c1","optional":false},{"name":"kind","type":"05205074b24d8340","optional":false},{"name":"through","type":"0657d3ca5ac4e7bf","optional":false}],"indices":[]},
"ba4c40b4381e0dcd": {"kind":"object","properties":[{"name":"form","type":"6f9e00383836275e","optional":false},{"name":"kind","type":"23af3759678a30de","optional":false},{"name":"sections","type":"c748fb42c80a1200","optional":false}],"indices":[]},
"ba77b58995e36f14": {"kind":"array","element":"07e1a58c58b593f5"},
"bab6865794007548": {"kind":"literal","value":"kill"},
@@ -1184,6 +1197,7 @@
"cbca49bde16b656d": {"kind":"literal","value":"block-start"},
"cd186c309e2bef30": {"kind":"literal","value":"approval/asked"},
"cda2345a9be9d139": {"kind":"object","properties":[{"name":"form","type":"e8de0f457fed5b9f","optional":false},{"name":"kind","type":"24b10f7cdae64928","optional":false},{"name":"summary","type":"4cd48f3e5108bd6e","optional":false}],"indices":[]},
"cda79b839d8cc0c7": {"kind":"union","types":["09f3f8dc417f2782","4b259f7f8f0a44ad","7580e7d96dc8475f","b782058cef83caf3","fa87537f4a9c1e25"]},
"cdca80b8dad24d4e": {"kind":"literal","value":"tool-workflow/run-start"},
"cdee10e87dbed844": {"kind":"object","properties":[{"name":"delayMs","type":"0657d3ca5ac4e7bf","optional":false},{"name":"failure","type":"5f2117d1856a5ab4","optional":false},{"name":"maxRetries","type":"0657d3ca5ac4e7bf","optional":false},{"name":"mode","type":"3bc287e765d58dc3","optional":false},{"name":"policyKey","type":"4cd48f3e5108bd6e","optional":false},{"name":"provider","type":"4cd48f3e5108bd6e","optional":false},{"name":"retry","type":"0657d3ca5ac4e7bf","optional":false},{"name":"retryId","type":"4cd48f3e5108bd6e","optional":false},{"name":"step","type":"0657d3ca5ac4e7bf","optional":false},{"name":"turn","type":"0657d3ca5ac4e7bf","optional":false}],"indices":[]},
"cef171adf021a186": {"kind":"object","properties":[{"name":"arguments","type":"bbc5696e3f13c758","optional":false},{"name":"content","type":"b52f215a6cd69105","optional":false},{"name":"error","type":"a81059ca06999da1","optional":true},{"name":"isError","type":"aa93010d0e521ad7","optional":false},{"name":"meta","type":"c592ce75aab73fca","optional":true},{"name":"name","type":"4cd48f3e5108bd6e","optional":false},{"name":"parentCallId","type":"4cd48f3e5108bd6e","optional":false},{"name":"rootCallId","type":"4cd48f3e5108bd6e","optional":false},{"name":"subCallId","type":"4cd48f3e5108bd6e","optional":false}],"indices":[]},
@@ -1208,9 +1222,11 @@
"db874d463b0fdec7": {"kind":"object","properties":[{"name":"data","type":"3e4e7683b0192c9f","optional":false},{"name":"ignorable","type":"dacf3d5f3e4ff619","optional":true},{"name":"seq","type":"0657d3ca5ac4e7bf","optional":false},{"name":"time","type":"0657d3ca5ac4e7bf","optional":false},{"name":"type","type":"e2d0367e5716bc18","optional":false}],"indices":[]},
"dba54c09c6e7aafc": {"kind":"object","properties":[{"name":"kind","type":"4edec1b770e8ec4c","optional":false}],"indices":[]},
"dbdafceb9a900183": {"kind":"literal","value":"hook"},
"dd00cdccd0ba487b": {"kind":"object","properties":[{"name":"form","type":"aec150797b655c5f","optional":false},{"name":"kind","type":"05205074b24d8340","optional":false},{"name":"through","type":"0657d3ca5ac4e7bf","optional":false}],"indices":[]},
"dd2780e7e0768433": {"kind":"literal","value":"fresh"},
"dd5ee8762bd8a14b": {"kind":"object","properties":[{"name":"form","type":"aec150797b655c5f","optional":false},{"name":"kind","type":"99e7e7d7fed55ae6","optional":false}],"indices":[]},
"dd6cc2bb2a8f2824": {"kind":"object","properties":[{"name":"form","type":"383cf73ae4748e5b","optional":false},{"name":"kind","type":"99e7e7d7fed55ae6","optional":false}],"indices":[]},
"dde0cdf9d426768b": {"kind":"object","properties":[{"name":"kind","type":"05205074b24d8340","optional":false},{"name":"through","type":"0657d3ca5ac4e7bf","optional":false}],"indices":[]},
"de25352d2dc41963": {"kind":"literal","value":"user/message"},
"de30b1fa12c75f81": {"kind":"literal","value":"image/gif"},
"deaae5cd30987d4f": {"kind":"object","properties":[{"name":"form","type":"16d0507cb94b4cfc","optional":false},{"name":"kind","type":"0fe52387f6505270","optional":false}],"indices":[]},
@@ -1240,6 +1256,7 @@
"e7ce49f12953583f": {"kind":"literal","value":"initial"},
"e7e154d47d5fbc53": {"kind":"literal","value":"clear"},
"e7e8f1318170b828": {"kind":"literal","value":"deleted"},
"e7f4cbb3a57f4cbd": {"kind":"object","properties":[{"name":"form","type":"e8de0f457fed5b9f","optional":false},{"name":"kind","type":"05205074b24d8340","optional":false},{"name":"summary","type":"4cd48f3e5108bd6e","optional":false},{"name":"through","type":"0657d3ca5ac4e7bf","optional":false}],"indices":[]},
"e8de0f457fed5b9f": {"kind":"literal","value":"notice"},
"e95f957154b2b1b5": {"kind":"literal","value":"assistant/attempt"},
"ea08ec1df0c33108": {"kind":"object","properties":[{"name":"message","type":"2eafefdc6f79a492","optional":false},{"name":"teamId","type":"4cd48f3e5108bd6e","optional":false},{"name":"version","type":"a0f906856fc06f1c","optional":false}],"indices":[]},
@@ -1255,6 +1272,7 @@
"ef11a3b7fa2033ae": {"kind":"object","properties":[{"name":"step","type":"0657d3ca5ac4e7bf","optional":false},{"name":"turn","type":"0657d3ca5ac4e7bf","optional":false}],"indices":[]},
"ef226a90ddac6cd8": {"kind":"literal","value":"killed"},
"ef4f00923b9edbae": {"kind":"literal","value":"compaction/summary"},
"efa1a091017ce533": {"kind":"object","properties":[{"name":"data","type":"a3e299483348f27a","optional":false},{"name":"ignorable","type":"dacf3d5f3e4ff619","optional":true},{"name":"seq","type":"0657d3ca5ac4e7bf","optional":false},{"name":"time","type":"0657d3ca5ac4e7bf","optional":false},{"name":"type","type":"587bb11da730e06a","optional":false}],"indices":[]},
"f05fb85e715af4ab": {"kind":"literal","value":"task-result"},
"f09e5e7acf5ff5cc": {"kind":"object","properties":[{"name":"model","type":"4cd48f3e5108bd6e","optional":false},{"name":"provider","type":"4cd48f3e5108bd6e","optional":false},{"name":"reasoningEffort","type":"4cd48f3e5108bd6e","optional":true}],"indices":[]},
"f1687f982fa3d41e": {"kind":"union","types":["023a355b2be0d688","0a30c3be336cdd4f","56b7c047694875a8","79650cfa10aa3c52","ab7ec5f9eb2c6dfa","b6ef9c6210be8350","ec8b5ff7c77ca689"]},
@@ -1266,7 +1284,6 @@
"f2da0f27c1e6add5": {"kind":"object","properties":[{"name":"attachment","type":"f19bee84bf3d2090","optional":false},{"name":"type","type":"277b61f9c6f81704","optional":false}],"indices":[]},
"f311671e07bc7fa6": {"kind":"literal","value":"in-history"},
"f4a66f654996c093": {"kind":"object","properties":[{"name":"kind","type":"0fe52387f6505270","optional":false}],"indices":[]},
"f4ed4717b4c259e9": {"kind":"object","properties":[{"name":"headerSeq","type":"0657d3ca5ac4e7bf","optional":true},{"name":"message","type":"0f6265188dae7ba6","optional":false},{"name":"step","type":"0657d3ca5ac4e7bf","optional":false},{"name":"turn","type":"0657d3ca5ac4e7bf","optional":false}],"indices":[]},
"f5520c1d261ca86e": {"kind":"object","properties":[{"name":"data","type":"e3056e0dda3a379e","optional":false},{"name":"ignorable","type":"dacf3d5f3e4ff619","optional":true},{"name":"seq","type":"0657d3ca5ac4e7bf","optional":false},{"name":"time","type":"0657d3ca5ac4e7bf","optional":false},{"name":"type","type":"24a8188c4d61b70a","optional":false}],"indices":[]},
"f56c5b59ca016131": {"kind":"object","properties":[{"name":"form","type":"383cf73ae4748e5b","optional":false},{"name":"kind","type":"7f13860ee89a2d35","optional":false},{"name":"references","type":"df843efd87c78cf8","optional":false},{"name":"version","type":"f88f1b7b6bb733f6","optional":false}],"indices":[]},
"f6329fb30c7ea801": {"kind":"object","properties":[{"name":"form","type":"383cf73ae4748e5b","optional":false},{"name":"kind","type":"0fe52387f6505270","optional":false}],"indices":[]},
@@ -1282,7 +1299,6 @@
"f90eb4ab2d3897bc": {"kind":"object","properties":[{"name":"turn","type":"0657d3ca5ac4e7bf","optional":false}],"indices":[]},
"f97f47b0eb87905e": {"kind":"object","properties":[{"name":"form","type":"16d0507cb94b4cfc","optional":false},{"name":"kind","type":"78a9be340090b735","optional":false}],"indices":[]},
"f9cfc89e8fe085b4": {"kind":"literal","value":"text-chunks"},
"fa106c5b33b657e8": {"kind":"object","properties":[{"name":"inserted","type":"454a0fa879e38076","optional":false},{"name":"outcome","type":"d4e4d575abeb4b72","optional":true},{"name":"removedCount","type":"0657d3ca5ac4e7bf","optional":true},{"name":"start","type":"0657d3ca5ac4e7bf","optional":false},{"name":"target","type":"96f4a9c81fc0f940","optional":false}],"indices":[]},
"fa87537f4a9c1e25": {"kind":"object","properties":[{"name":"content","type":"b52f215a6cd69105","optional":false},{"name":"id","type":"4cd48f3e5108bd6e","optional":false},{"name":"role","type":"95c0e88fcf0bafb2","optional":false},{"name":"source","type":"719c51f67074f970","optional":false}],"indices":[]},
"fab2bb09bc5e3b9d": {"kind":"union","types":["12a0cfa619ac4a5f","18fce9540bb20897","a3edd1efd1fef21b"]},
"fab35c4520883c0d": {"kind":"literal","value":"rejected"},
+67
View File
@@ -69,6 +69,53 @@ interface PendingModule {
Generated from source by `scripts/gen-cordis-catalog.ts` (verified fresh by `pnpm run verify-cordis-catalog` in doc-sync; regenerate with `pnpm run gen-cordis-catalog`) — the language sides differ only in locale-specific paired document paths. Signature blocks use a `ts cordis-catalog` fence and keep the original source JSDoc; dispatch modes are defined in the [primer](../cordis-primer.md#dispatch-modes), and the framework-inherited `ctx` API lives in [cordis-api/inherited.md](../cordis-api/inherited.md).
<a id="ctxactioncards--actioncards"></a>
### `ctx.actionCards` — `ActionCards`
Card tool, kind registry, Session projection, user actions and status notices.
```ts cordis-catalog
/**
* Register one action kind for the caller's lifetime.
* @param kind - the kind's skill, details schema, verbs and handler.
* @returns disposer removing the kind; actions on its cards are then unavailable.
* @throws when another kind already registered the same action.
*/
register<Details>(kind: ActionCardKind<Details>): () => void
/**
* Record a card decision in the Session log; the next model request reports it.
* @param agent - agent whose Session owns the card.
* @param decision - the card's new state.
* @throws when no kind ever registered the decision's action with this service, or its phase is one the projection derives.
*/
record(agent: Agent, decision: ActionCardDecision): void
/**
* Read the cards of one Session.
* @param session - a Session whose projections are prepared.
* @returns proposals and decisions by call id.
*/
view(session: Session): ActionCardView
/**
* Apply a user action to a logged card. Shared checks run first, then the kind reconciles the card; `dismiss` is
* recorded here and every other verb goes to the card's kind. `dismiss` also works on an unstarted card whose kind
* is no longer registered. Concurrent actions on one card share one operation.
* @param agent - live root resolved from the displayed Session.
* @param callId - the card's `propose_action` call id.
* @param verb - `dismiss` or one of the kind's verbs.
* @param payload - kind-defined fields such as a chosen location.
* @returns acceptance of background work, the decision in effect, or a refusal.
*/
@Remote act(agent: Agent, callId: ToolCallId, verb: string, payload?: Record<string, JsonValue>): Promise<ActionCardResult>
```
Types: [Agent](core.md) · [Session](session.md) · [ToolCallId](core.md)
Source: [`packages/experimental/action-cards/src/index.ts`](../../packages/experimental/action-cards/src/index.ts)
<a id="ctxconfigeditor--configeditor"></a>
### `ctx.configEditor` — `ConfigEditor`
@@ -355,6 +402,26 @@ async entry(url: string, name: string): Promise<MarketListingEntry | undefined>
Source: [`packages/boot/plugin-market/src/index.ts`](../../packages/boot/plugin-market/src/index.ts)
<a id="ctxpluginrecommendations--pluginrecommendations"></a>
### `ctx.pluginRecommendations` — `PluginRecommendations`
Catalog lookups for card text and the enabling behind the `enable_official_plugin` card.
```ts cordis-catalog
/**
* Read the current catalog text for a logged card.
* @param agent - Exact live root resolved by the Gateway from the Session id.
* @param callId - The card's `propose_action` call id, never a package supplied by the browser.
* @returns the plugin's title, description and selection, or a refusal when the card names no available official plugin.
*/
@Remote async describe(agent: Agent, callId: ToolCallId): Promise<RecommendationDetails>
```
Types: [Agent](core.md) · [ToolCallId](core.md)
Source: [`packages/experimental/plugin-recommendations/src/index.ts`](../../packages/experimental/plugin-recommendations/src/index.ts)
<a id="ctxpluginregistryprobe--pluginregistryprobe"></a>
### `ctx.pluginRegistryProbe` — `PluginRegistryProbe`
+67
View File
@@ -69,6 +69,53 @@ interface PendingModule {
Generated from source by `scripts/gen-cordis-catalog.ts` (verified fresh by `pnpm run verify-cordis-catalog` in doc-sync; regenerate with `pnpm run gen-cordis-catalog`) — the language sides differ only in locale-specific paired document paths. Signature blocks use a `ts cordis-catalog` fence and keep the original source JSDoc; dispatch modes are defined in the [primer](../cordis-primer.zh.md#dispatch-modes), and the framework-inherited `ctx` API lives in [cordis-api/inherited.md](../cordis-api/inherited.md).
<a id="ctxactioncards--actioncards"></a>
### `ctx.actionCards` — `ActionCards`
Card tool, kind registry, Session projection, user actions and status notices.
```ts cordis-catalog
/**
* Register one action kind for the caller's lifetime.
* @param kind - the kind's skill, details schema, verbs and handler.
* @returns disposer removing the kind; actions on its cards are then unavailable.
* @throws when another kind already registered the same action.
*/
register<Details>(kind: ActionCardKind<Details>): () => void
/**
* Record a card decision in the Session log; the next model request reports it.
* @param agent - agent whose Session owns the card.
* @param decision - the card's new state.
* @throws when no kind ever registered the decision's action with this service, or its phase is one the projection derives.
*/
record(agent: Agent, decision: ActionCardDecision): void
/**
* Read the cards of one Session.
* @param session - a Session whose projections are prepared.
* @returns proposals and decisions by call id.
*/
view(session: Session): ActionCardView
/**
* Apply a user action to a logged card. Shared checks run first, then the kind reconciles the card; `dismiss` is
* recorded here and every other verb goes to the card's kind. `dismiss` also works on an unstarted card whose kind
* is no longer registered. Concurrent actions on one card share one operation.
* @param agent - live root resolved from the displayed Session.
* @param callId - the card's `propose_action` call id.
* @param verb - `dismiss` or one of the kind's verbs.
* @param payload - kind-defined fields such as a chosen location.
* @returns acceptance of background work, the decision in effect, or a refusal.
*/
@Remote act(agent: Agent, callId: ToolCallId, verb: string, payload?: Record<string, JsonValue>): Promise<ActionCardResult>
```
Types: [Agent](core.zh.md) · [Session](session.zh.md) · [ToolCallId](core.zh.md)
Source: [`packages/experimental/action-cards/src/index.ts`](../../packages/experimental/action-cards/src/index.ts)
<a id="ctxconfigeditor--configeditor"></a>
### `ctx.configEditor` — `ConfigEditor`
@@ -355,6 +402,26 @@ async entry(url: string, name: string): Promise<MarketListingEntry | undefined>
Source: [`packages/boot/plugin-market/src/index.ts`](../../packages/boot/plugin-market/src/index.ts)
<a id="ctxpluginrecommendations--pluginrecommendations"></a>
### `ctx.pluginRecommendations` — `PluginRecommendations`
Catalog lookups for card text and the enabling behind the `enable_official_plugin` card.
```ts cordis-catalog
/**
* Read the current catalog text for a logged card.
* @param agent - Exact live root resolved by the Gateway from the Session id.
* @param callId - The card's `propose_action` call id, never a package supplied by the browser.
* @returns the plugin's title, description and selection, or a refusal when the card names no available official plugin.
*/
@Remote async describe(agent: Agent, callId: ToolCallId): Promise<RecommendationDetails>
```
Types: [Agent](core.zh.md) · [ToolCallId](core.zh.md)
Source: [`packages/experimental/plugin-recommendations/src/index.ts`](../../packages/experimental/plugin-recommendations/src/index.ts)
<a id="ctxpluginregistryprobe--pluginregistryprobe"></a>
### `ctx.pluginRegistryProbe` — `PluginRegistryProbe`
+10 -4
View File
@@ -9,8 +9,8 @@
en: 6cc2cd9ced70bb09
zh: 1c0e6cb4ba2ed637
/tool-schema-catalog/tool-package-map:
en: 10fe940a3d17cde7
zh: ef1e8863c28e3c05
en: c089eeb9ebe832b1
zh: 97f6ac552abb55e3
/tool-schema-catalog/deepseek-ai-dsh-plugin-manager:
en: e0143916bb634762
zh: e0143916bb634762
@@ -54,8 +54,14 @@
en: 4910ab327694ee97
zh: 17e79e4366cb636d
/tool-schema-catalog/deepseek-ai-dsh-experimental-browser-use-stagehand-native/stagehand-tabs:
en: 095b18f9376e073d
zh: abd8785f8acaa555
en: a0d5b9b891358120
zh: 25793a62709770b5
/tool-schema-catalog/deepseek-ai-dsh-action-cards:
en: 60c9df6ffda98a1d
zh: 60c9df6ffda98a1d
/tool-schema-catalog/deepseek-ai-dsh-action-cards/propose-action:
en: c289411d447b015a
zh: 3a770388e90df4df
/tool-schema-catalog/deepseek-ai-dsh-tool-ask-user:
en: e713a4a11a15d13c
zh: e713a4a11a15d13c
+44
View File
@@ -19,6 +19,7 @@ This table connects model-visible tool names to the plugin package and service s
| `@deepseek-ai/dsh-plugin-market` | `plugin_market` | `ctx.tools`, `ctx.pluginMarket` | `tool/call`, `tool/result` | - | - |
| `@deepseek-ai/dsh-mcp-resources` | `list_mcp_resource_templates`, `list_mcp_resources`, `read_mcp_resource` | `ctx.tools`, `ctx.mcpResources` | `tool/call`, `tool/result` | - | - |
| `@deepseek-ai/dsh-experimental-browser-use-stagehand-native` | `stagehand_act`, `stagehand_extract`, `stagehand_navigate`, `stagehand_observe`, `stagehand_screenshot`, `stagehand_tabs` | `ctx.browserUse`, `ctx.agents`, `ctx.tools`, `ctx.systemPrompt` | `tool/call`, `tool/result` | - | - |
| `@deepseek-ai/dsh-action-cards` | `propose_action` | `ctx.tools`, `ctx.agents`, `ctx.sessionProjections`, `ctx.remoteAccess` | `tool/call`, `tool/result`, `plugin:action-cards/decision`, `user/message` | - | Declared only to an Agent that loaded the skill of a registered card kind; the kind packages register their actions, and the Personalized pages and Plugin recommendations bundles supply the shipped kinds. |
| `@deepseek-ai/dsh-tool-ask-user` | `ask_user_question` | `ctx.tools`, `ctx.userQuestions` | `tool/call`, `tool/result after an answer or timeout`, `late user/message` | - | ask_user_question keeps the original blocking behavior by default; set `mode: timed` to opt into a foreground timeout and pending result while the question remains answerable. In timed mode, `timeout: -1` keeps that call blocking indefinitely. |
| `@deepseek-ai/dsh-tool-executor-ptc` | `run_code` | `ctx.workingDirectory (Agent executions)`, `ctx.tools`, `ctx.ptcRuntime`, `ctx.systemPrompt` | `tool/call`, `one tool/ptc-dispatch-start + tool/ptc-dispatch pair per bridged sub-call`, `tool/result` | - | The PTC executor exposes only run_code to the model and declares other visible capabilities in a generated SDK in the runtime’s language. Program bindings enter the shared guarded tool pipeline in submission order; concurrency-safe bodies overlap up to maxParallelSubCalls, and nested results remain linked to the outer program. |
| `@deepseek-ai/dsh-plan-mode` | `exit_plan_mode` | `ctx.tools`, `ctx.systemPrompt`, `ctx.agents`, `ctx.userQuestions (execution time, opportunistic)` | `tool/call`, `plan/mode inactive on an approved review`, `tool/result` | - | exit_plan_mode stays in the model-facing schema while planning is inactive so transitions add no tool-catalog churn on top of the plan-policy change. Its execute path rejects calls outside plan mode; in plan mode it presents the plan over the user-questions seam (approve / keep planning with feedback), and approval logs plan mode inactive at the step boundary. |
@@ -497,6 +498,49 @@ List, create, select, or close a Stagehand browser tab.
Source: [`packages/experimental/browser-use-stagehand-native/src/index.ts`](../packages/experimental/browser-use-stagehand-native/src/index.ts)
<a id="deepseek-aidsh-action-cards"></a>
## `@deepseek-ai/dsh-action-cards`
### `propose_action`
Show the user one optional action card under this answer. Call only as the skill that names the action instructs.
```json
{
"type": "object",
"properties": {
"action": {
"type": "string",
"description": "Action name from the skill."
},
"title": {
"type": "string",
"description": "Card title."
},
"brief": {
"type": "string",
"description": "What the action does, in the user's language."
},
"details": {
"type": "object",
"description": "Fields the skill lists for this action.",
"additionalProperties": true
}
},
"required": [
"action",
"title",
"brief",
"details"
]
}
```
Source: [`packages/experimental/action-cards/src/index.ts`](../packages/experimental/action-cards/src/index.ts)
Declared only to an Agent that loaded the skill of a registered card kind; the kind packages register their actions, and the Personalized pages and Plugin recommendations bundles supply the shipped kinds.
<a id="deepseek-aidsh-tool-ask-user"></a>
## `@deepseek-ai/dsh-tool-ask-user`
+44
View File
@@ -23,6 +23,7 @@
| `@deepseek-ai/dsh-plugin-market` | `plugin_market` | `ctx.tools`, `ctx.pluginMarket` | `tool/call`, `tool/result` | - | - |
| `@deepseek-ai/dsh-mcp-resources` | `list_mcp_resource_templates`, `list_mcp_resources`, `read_mcp_resource` | `ctx.tools`, `ctx.mcpResources` | `tool/call`, `tool/result` | - | - |
| `@deepseek-ai/dsh-experimental-browser-use-stagehand-native` | `stagehand_act`、`stagehand_extract`、`stagehand_navigate`、`stagehand_observe`、`stagehand_screenshot`、`stagehand_tabs` | `ctx.browserUse`、`ctx.agents`、`ctx.tools`、`ctx.systemPrompt` | `tool/call`、`tool/result` | - | - |
| `@deepseek-ai/dsh-action-cards` | `propose_action` | `ctx.tools`、`ctx.agents`、`ctx.sessionProjections`、`ctx.remoteAccess` | `tool/call`、`tool/result`、`plugin:action-cards/decision`、`user/message` | - | 只向加载过已注册卡片种类 Skill 的 Agent 声明;各种类的包注册自己的动作,个性化页面和插件推荐两个 Bundle 提供随产品发布的种类。 |
| `@deepseek-ai/dsh-tool-ask-user` | `ask_user_question` | `ctx.tools`、`ctx.userQuestions` | `tool/call`、`tool/result after an answer or timeout`、`late user/message` | - | ask_user_question 默认保持原有阻塞行为;设置 `mode: timed` 后才启用前台超时与 pending 结果,同时问题仍可回答;timed 模式内 `timeout: -1` 让本次调用无限期阻塞。 |
| `@deepseek-ai/dsh-tool-executor-ptc` | `run_code` | `ctx.workingDirectory (Agent executions)`、`ctx.tools`、`ctx.ptcRuntime`、`ctx.systemPrompt` | `tool/call`、`one tool/ptc-dispatch-start + tool/ptc-dispatch pair per bridged sub-call`、`tool/result` | - | PTC 执行器只向模型公开 run_code,并在运行时语言生成的 SDK 中声明其他可见能力。程序绑定按提交顺序进入共享的受守卫工具流水线;并行安全的函数体最多重叠 maxParallelSubCalls 个,嵌套结果保持与外层程序关联。 |
| `@deepseek-ai/dsh-plan-mode` | `exit_plan_mode` | `ctx.tools`、`ctx.systemPrompt`、`ctx.agents`、`ctx.userQuestions (execution time, opportunistic)` | `tool/call`、`plan/mode inactive on an approved review`、`tool/result` | - | 规划未激活时,exit_plan_mode 仍保留在面向模型的 schema 中,这样状态转换不会在规划策略变更之外额外造成工具目录变动。其执行路径会拒绝规划模式之外的调用;在规划模式下,它通过用户交互 seam 提交计划(批准/根据反馈继续规划),批准后会在步骤边界记录规划模式已停用。 |
@@ -501,6 +502,49 @@
来源:[`packages/experimental/browser-use-stagehand-native/src/index.ts`](../packages/experimental/browser-use-stagehand-native/src/index.ts)
<a id="deepseek-aidsh-action-cards"></a>
## `@deepseek-ai/dsh-action-cards`
### `propose_action`
在本次回答下方向用户展示一张可选操作卡片。只按指定该操作的 Skill 的要求调用。
```json
{
"type": "object",
"properties": {
"action": {
"type": "string",
"description": "Action name from the skill."
},
"title": {
"type": "string",
"description": "Card title."
},
"brief": {
"type": "string",
"description": "What the action does, in the user's language."
},
"details": {
"type": "object",
"description": "Fields the skill lists for this action.",
"additionalProperties": true
}
},
"required": [
"action",
"title",
"brief",
"details"
]
}
```
来源:[`packages/experimental/action-cards/src/index.ts`](../packages/experimental/action-cards/src/index.ts)
只向加载过已注册卡片种类 Skill 的 Agent 声明;各种类的包注册自己的动作,个性化页面和插件推荐两个 Bundle 提供随产品发布的种类。
<a id="deepseek-aidsh-tool-ask-user"></a>
## `@deepseek-ai/dsh-tool-ask-user`
+2 -1
View File
@@ -300,6 +300,7 @@
"@deepseek-ai/dsh-cua-bundle": "workspace:*",
"@deepseek-ai/dsh-experimental-subagent-oar": "workspace:*",
"@deepseek-ai/dsh-experimental-tool-subagent-oar": "workspace:*",
"@deepseek-ai/dsh-experimental-cua-macos": "workspace:*"
"@deepseek-ai/dsh-experimental-cua-macos": "workspace:*",
"@deepseek-ai/dsh-action-cards": "workspace:*"
}
}
+2 -2
View File
@@ -15,8 +15,8 @@
en: ffc55792d6e922e1
zh: 83bd759638857e2d
/packages/package-groups:
en: d67996086e3b4f28
zh: cb758b1fcee5ab3e
en: c397fc0c1885058a
zh: c720d0e91dd1d15e
/packages/release-expectations:
en: 236081c1659b25ab
zh: 809110adf003802e
+2 -2
View File
@@ -75,11 +75,11 @@ Every package lives in exactly one group; new packages join existing groups, and
| [`sdk/`](sdk/README.md) | Out-of-process SDK: JSON-RPC protocol and TypeScript client/server |
| [`acp/`](acp/README.md) | Automation-only Agent Client Protocol server |
| [`interaction/`](interaction/README.md) | Human-collaboration plane: approval/interaction seams, permission preset, commands, ask-user tool |
| [`boot/`](boot/README.md) | Shared app-bin boot glue and installation bundle lists |
| [`boot/`](boot/README.md) | Shared app-bin boot glue |
| [`host/`](host/README.md) | Web GUI host services, directory picking, application launch, plugin inventory, and product telemetry |
| [`client/`](client/README.md) | Web-GUI browser half: shell, wire, object services, slots, `ui-*` plugins |
| [`test-support/`](test-support/README.md) | Test infrastructure (testkits, replay, Loader smokes) |
| [`util/`](util/README.md) | Low-level zero-dependency utilities shared across groups (`Branded<B>`, home/path helpers, timeout, retention) |
| [`util/`](util/README.md) | Low-level utilities and data-only package lists shared across groups (`Branded<B>`, home/path helpers, timeout, retention, official bundle lists) |
-----
+2 -2
View File
@@ -75,11 +75,11 @@ harness 由 `packages/` 下的 npm 包组装而成,按能力系列分组:会
| [`sdk/`](sdk/README.zh.md) | 进程外 SDK:JSON-RPC 协议与 TypeScript 客户端/服务器 |
| [`acp/`](acp/README.zh.md) | 仅面向自动化的 ACP(Agent Client Protocol)服务器 |
| [`interaction/`](interaction/README.zh.md) | 人机协作平面:批准/交互 seam、权限预设、命令、询问用户的工具 |
| [`boot/`](boot/README.zh.md) | 共享的 app bin 启动粘合层与安装方组合包列表 |
| [`boot/`](boot/README.zh.md) | 共享的 app bin 启动粘合层 |
| [`host/`](host/README.zh.md) | Web GUI 宿主服务、目录选择、应用启动、插件清单和产品遥测 |
| [`client/`](client/README.zh.md) | web GUI 浏览器半侧:shell、协议层、对象服务、slot、`ui-*` 插件 |
| [`test-support/`](test-support/README.zh.md) | 测试基础设施(testkit、回放、Loader 冒烟测试) |
| [`util/`](util/README.zh.md) | 组间共享的低层零依赖工具(`Branded<B>`、home/路径辅助函数、超时、留存) |
| [`util/`](util/README.zh.md) | 组间共享的低层工具与纯数据包列表(`Branded<B>`、home/路径辅助函数、超时、留存、官方组合包列表) |
-----
@@ -100,7 +100,7 @@ function AddPluginMenu({ t, disabled, openInstall, renderSlot }: {
const HIGHLIGHT_MS = 2_400
/**
* Whether the main list shows an official optional bundle: the always-held ones, and any held-back one the profile enabled.
* Whether the main list shows an official optional bundle: any bundle outside `MORE_BUNDLES`, and any held-back one the profile enabled.
* @param pkg - the official bundle as the Host reports it.
* @returns whether the main list shows it.
*/
@@ -109,14 +109,14 @@ function pinnedToMainList(pkg: PackageView): boolean {
}
/**
* Where an optional bundle sits on both lists: the always-held ones in their order, then the bundles neither list
* Where an optional bundle sits on both lists: the `EXTENSION_BUNDLES` in their order, then the bundles neither list
* ranks, then the held-back ones in theirs, so Codex and Claude Code stay last on the More list.
* @param name - the bundle's package name.
* @returns the rank; bundles of equal rank keep the order the Host reported.
*/
function optionalBundleRank(name: string): number {
const alwaysHeld = EXTENSION_BUNDLES.indexOf(name)
if (alwaysHeld >= 0) return alwaysHeld
const ranked = EXTENSION_BUNDLES.indexOf(name)
if (ranked >= 0) return ranked
const heldBack = MORE_BUNDLES.indexOf(name)
return heldBack < 0 ? EXTENSION_BUNDLES.length : EXTENSION_BUNDLES.length + 1 + heldBack
}
@@ -1237,8 +1237,11 @@ describe('PluginManagerPage', () => {
expect(document.querySelector(`[data-plugin-package="${name}"]`)).toBeTruthy()
})
it('returns from package details to the secondary page and retains it across React remounts', () => {
const name = '@deepseek-ai/dsh-experimental-session-titles-bundle'
it.each([
'@deepseek-ai/dsh-experimental-session-titles-bundle',
'@deepseek-ai/dsh-creation-recommendations',
'@deepseek-ai/dsh-plugin-recommendations',
])('returns %s details to the secondary page and retains it across React remounts', (name) => {
const b = renderTab({ packages: [pkg({ name, official: true, enabled: false })] })
fireEvent.click(screen.getByRole('button', { name: en.moreAction }))
expect(screen.getByRole('heading', { name: en.moreTitle, level: 1 })).toBeTruthy()
@@ -1253,9 +1256,12 @@ describe('PluginManagerPage', () => {
expect(document.querySelector(`[data-plugin-package="${name}"]`)).toBeNull()
})
it('pins a bundle the profile picks onto the main list, in its full-list place', () => {
it.each([
'@deepseek-ai/dsh-experimental-tool-worktree',
'@deepseek-ai/dsh-creation-recommendations',
'@deepseek-ai/dsh-plugin-recommendations',
])('pins enabled %s onto the main list and removes it when disabled', (first) => {
const team = '@deepseek-ai/dsh-experimental-agent-team-profile'
const first = '@deepseek-ai/dsh-experimental-tool-worktree'
const second = '@deepseek-ai/dsh-experimental-cot-translation-bundle'
const entry = (name: string, enabled: boolean): PackageView => pkg({ name, official: true, enabled })
const b = renderTab({ packages: [entry(second, true), entry(team, true), entry(first, false)] })
@@ -54,8 +54,8 @@
en: ca75c51c89c2c9b0
zh: de54f3467ef3b9be
/deepseek-ai-dsh-client-ui-primitives/known-limitations-and-deferred-work:
en: 5a1e9d041b556b0e
zh: c66b4e7d5f77162d
en: 9c5bc17e35af9d0b
zh: 4f1a142c7d56cf72
/deepseek-ai-dsh-client-ui-primitives/known-limitations-and-deferred-work/dev-note:
en: e67f633dc6fe7773
zh: 3bb0c20c93f16f98
+1
View File
@@ -220,6 +220,7 @@ These limits define how the atoms behave at the edges; they are current package
- **Streaming defers cross-boundary reference resolution** — a reference-style link or footnote whose definition sits on the other side of the incremental freeze boundary renders as literal text while the reply streams; the settled full parse at finalize resolves it.
- **A long highlighted fence retains its complete token DOM** — streaming avoids re-parsing, re-tokenizing, and reconciling the completed prefix, but it does not discard old colors or virtualize token spans. Final DOM cardinality therefore still follows the fence's token count; nested/container fences and a pathological single long line remain on the general tail path.
- **Overlong lines stay uncolored** — a source line of 1,000 or more UTF-16 units renders as one plain run in every highlighted surface, because TextMate scanning cost grows superlinearly with line length and a single generated line can otherwise block the page for seconds. The grammar state skips that line, so a comment or template literal that opens or closes on it mis-colors the following lines. Highlighting still runs synchronously during render, so a file with thousands of ordinary lines costs proportionally more.
- **The Go struct-field rule is patched locally** — when the Go grammar loads, its repeated one-line struct-field rule is replaced with the rule [worlpaker/go-syntax#25](https://github.com/worlpaker/go-syntax/pull/25) merged, because the vendored rule backtracks exponentially on lines such as a brace-bearing comment after `struct {`. If a dependency upgrade removes that rule, loading the grammar throws and Go code renders as plain text; delete the patch once `@shikijs/langs` ships the upstream rule.
- **The fish logo is a redrawn approximation** — its font glyph has no exportable vector geometry in the local design data, so a hand-authored recreation stands in until an exact export path exists.
- **`Pill` and `Input` have no design source** — both atoms are self-defined; the sidebar search field and view-tab strip that resemble them are consumer-owned compositions, not these atoms.
- **Product icons need the ui-theme sprite** — each icon only references its symbol, so without ui-theme, or inside another document such as an iframe, the icon renders nothing.
@@ -220,6 +220,7 @@ kind: "package-library"
- **流式期间跨边界引用解析被推迟**:定义落在增量冻结边界另一侧的引用式链接或脚注,在回复流式输出期间渲染为字面文本;定稿时的全量解析会将其解析。
- **长高亮 fence 会保留完整 token DOM**:流式路径避免重新解析、重新 tokenize 和 reconcile 已完成前缀,但不会丢弃旧颜色或虚拟化 token span。因此最终 DOM 数量仍随 fence 的 token 数增长;嵌套/容器内 fence 与病态的单个超长行仍走通用尾部路径。
- **超长行不着色**:所有高亮界面中,长度达到 1,000 个 UTF-16 单元的源码行渲染为一段纯文本,因为 TextMate 扫描成本随行长超线性增长,单个生成行否则会阻塞页面数秒。语法状态会跳过该行,因此在该行开始或结束的注释、模板字符串会使后续行着色错误。高亮仍在渲染期间同步执行,因此数千个普通行的文件仍按比例增加开销。
- **Go 结构体字段规则在本地打了补丁**:Go 语法加载时,重复匹配单行结构体字段的规则会替换为 [worlpaker/go-syntax#25](https://github.com/worlpaker/go-syntax/pull/25) 合入的规则,因为内置规则在 `struct {` 之后含花括号注释等行上会指数级回溯。若依赖升级后该规则消失,加载语法会抛错,Go 代码以纯文本显示;`@shikijs/langs` 发布上游规则后删除此补丁。
- **鱼形标志是重新绘制的近似版本**:它来自字体字形,而本地设计数据无法导出其矢量几何;在获得精确导出路径前,使用手工重建版本代替。
- **`Pill` 与 `Input` 没有设计来源**:两个原子组件均自行定义;与其相似的侧边栏搜索字段和视图标签条由消费方组合,不是这些原子组件。
- **产品图标依赖 ui-theme 的 sprite**:每个图标只引用自己的 symbol,因此没有 ui-theme 时,或在 iframe 等另一个文档中,图标不渲染任何内容。
@@ -41,6 +41,39 @@ type LangModule = { default: typeof langTs }
*/
const LANGS = [langTs, langBash, langJson]
/**
* Replace the Go grammar's repeated one-line struct-field rule with the rule
* worlpaker/go-syntax merged in 4a9b120a52c (https://github.com/worlpaker/go-syntax/pull/25):
* the repeated rule backtracks exponentially on lines such as a brace-bearing
* comment after `struct {`. Delete this patch once `@shikijs/langs` ships that
* commit; 4.5.0 does not. The rule is located as the only rule in
* `struct_variables_types_fields` containing the field-token literal below,
* taken from `@shikijs/langs` 4.4.3.
* @param grammars - the `@shikijs/langs/go` default export, left unmodified.
* @returns copies of `grammars` with that rule's match replaced in the `go` grammar.
* @throws When the module has no `go` grammar, the grammar lacks
* `struct_variables_types_fields`, or that entry has no or several such rules.
* The lazy load then rejects unhandled and Go code renders as plain text.
*/
export function patchGoGrammar(grammars: LangModule['default']): LangModule['default'] {
const go = grammars.find(grammar => grammar.name === 'go')
const fields = go?.repository.struct_variables_types_fields
const patterns = fields?.patterns ?? []
const rules = patterns.filter(rule => typeof rule.match === 'string' && rule.match.includes('[^"/`\\s]+;?'))
if (go === undefined || fields === undefined || rules.length !== 1) {
throw new Error(`ui-primitives: expected exactly one Go struct-field rule to patch for worlpaker/go-syntax#25, found ${
rules.length}; update or delete the patch`)
}
const repository = { ...go.repository }
repository.struct_variables_types_fields = {
...fields,
patterns: patterns.map(rule => rule === rules[0]
? { ...rule, match: '(?:(?<=\\{)([^/\\`"]*[^\\s/\\`"])\\s*(?=\\}))' }
: rule),
}
return grammars.map(grammar => grammar === go ? { ...go, repository } : grammar)
}
/**
* The non-boot extension grammars, each behind a dynamic import so its module
* stays out of the boot chunk until a code surface renders that language.
@@ -53,7 +86,7 @@ const LANGS = [langTs, langBash, langJson]
const LAZY_GRAMMARS = new Map<string, () => Promise<LangModule>>([
['python', () => import('@shikijs/langs/python')],
['ruby', () => import('@shikijs/langs/ruby')],
['go', () => import('@shikijs/langs/go')],
['go', async () => ({ default: patchGoGrammar((await import('@shikijs/langs/go')).default) })],
['rust', () => import('@shikijs/langs/rust')],
['java', () => import('@shikijs/langs/java')],
['c', () => import('@shikijs/langs/c')],
@@ -0,0 +1,72 @@
/** Go regex matching runs in a terminable source-mode worker, not on the test runner's event loop. */
import { Worker } from 'node:worker_threads'
import { expect, it } from 'vitest'
import { patchGoGrammar } from '../src/markdown/highlight.ts'
import { GO_SOURCE } from './go-source.ts'
it('highlights struct comments with braces without blocking settled or streaming code', async () => {
// The worker and highlight.ts use erasable syntax and only package-name imports, so Node loads them directly.
const worker = new Worker(new URL('./go-highlighting.worker.ts', import.meta.url))
let timer: ReturnType<typeof setTimeout> | undefined
try {
const result = await new Promise<unknown>((resolve, reject) => {
const fail = (message: string): void => { reject(new Error(message)) }
timer = setTimeout(() => { fail('Go grammar did not load within 120 seconds') }, 120_000)
worker.on('message', (reply: unknown) => {
if (reply !== 'loaded') {
resolve(reply)
return
}
clearTimeout(timer)
// Bound runaway synchronous matching once imports and grammar loading are done.
timer = setTimeout(() => { fail('Go highlighting did not complete within 10 seconds') }, 10_000)
})
worker.once('error', reject)
worker.once('exit', (code) => { fail(`Go highlighting worker exited before its reply: ${code}`) })
})
expect(result).toMatchObject({ text: GO_SOURCE, streamingMatchesSettled: true })
expect(result).toHaveProperty('html', expect.stringContaining('var(--shiki-'))
const keyword = (...texts: string[]): string[] => texts.map(text => `${text}:keyword`)
expect(result).toHaveProperty('colored', [
['package:keyword', 'example:function'],
[],
['type:keyword', 'Record:function', 'struct:keyword'],
['// docs: {id} explains the ordinary field names for an example record ending here:comment'],
['int:keyword'],
[],
[],
['type:keyword', 'Inline:function', ...keyword('struct', 'string', 'int')],
['type:keyword', 'Nested:function', ...keyword('struct', 'map', 'string', 'struct', 'struct', 'int')],
['type:keyword', 'OneLineFields:function', ...keyword('struct', 'int', 'chan<-', 'struct', 'string'),
'sync:function', 'Mutex:function'],
['type:keyword', 'SplitOneLine:function', ...keyword('struct', 'int'), 'B:function', 'string:keyword'],
[],
[...keyword('var', '=', 'struct', 'string'), 'Port:function', 'int:keyword', 'Tags:function', 'string:keyword'],
[],
['type:keyword', 'Many:function', ...keyword('struct', ...Array<string>(24).fill('int'), 'struct', 'int')],
['type:keyword', 'Open:function', 'struct:keyword', ...Array<string>(47).fill('field:function')],
])
} finally {
clearTimeout(timer)
await worker.terminate()
}
}, 150_000)
it('patches exactly the vendored struct-field rule and rejects a grammar without it', async () => {
const { default: grammars } = await import('@shikijs/langs/go')
const other = { name: 'go-embedded', scopeName: 'source.go.embedded', patterns: [], repository: {} }
const before = grammars[0]?.repository.struct_variables_types_fields?.patterns
const patched = patchGoGrammar([...grammars, other])
const after = patched[0]?.repository.struct_variables_types_fields?.patterns
const changed = after?.flatMap((rule, index) => rule === before?.[index] ? [] : [[before?.[index]?.match, rule.match]])
expect(changed).toEqual([[
expect.stringContaining('[^"/`\\s]+;?'),
'(?:(?<=\\{)([^/\\`"]*[^\\s/\\`"])\\s*(?=\\}))',
]])
expect(patched[1]).toBe(other)
for (const repository of [{}, { struct_variables_types_fields: {} }]) {
expect(() => patchGoGrammar([{ name: 'go', scopeName: 'source.go', patterns: [], repository }]))
.toThrow('expected exactly one Go struct-field rule to patch for worlpaker/go-syntax#25, found 0')
}
expect(() => patchGoGrammar([other])).toThrow('found 0')
})
@@ -0,0 +1,31 @@
/** Source-mode highlighter fixture; the parent owns termination during synchronous regex matching. */
import { parentPort } from 'node:worker_threads'
import {
highlightLines, highlightToHtml, StreamingHighlightSession, subscribeGrammarLoaded,
} from '../src/markdown/highlight.ts'
import { GO_SOURCE } from './go-source.ts'
const ready = Promise.withResolvers<undefined>()
const dispose = subscribeGrammarLoaded(() => {
if (highlightLines('package example', 'go') !== undefined) ready.resolve(undefined)
})
try {
if (highlightLines('package example', 'go') === undefined) await ready.promise
} finally {
dispose()
}
// The parent times matching separately from imports and grammar loading.
parentPort?.postMessage('loaded')
const lines = highlightLines(GO_SOURCE, 'go')
const stream = new StreamingHighlightSession()
const split = GO_SOURCE.indexOf('\t//')
if (split <= 0) throw new Error('GO_SOURCE no longer contains the brace-bearing struct comment')
stream.update(GO_SOURCE.slice(0, split), 'go')
const streamed = stream.update(GO_SOURCE, 'go')
parentPort?.postMessage({
text: lines?.map(line => line.map(span => span.text).join('')).join('\n'),
colored: lines?.map(line => line.flatMap(span =>
span.style.color === 'var(--shiki-foreground)' ? [] : [`${span.text}:${String(span.style.color).replace('var(--shiki-token-', '').replace(')', '')}`])),
streamingMatchesSettled: JSON.stringify(streamed) === JSON.stringify(new StreamingHighlightSession().update(GO_SOURCE, 'go')),
html: highlightToHtml(GO_SOURCE, 'go'),
})
@@ -0,0 +1,21 @@
/** Synthetic Go struct comments and declarations for syntax-highlighting regressions. */
export const GO_SOURCE = [
'package example',
'',
'type Record struct {',
'\t// docs: {id} explains the ordinary field names for an example record ending here',
'\tValue int',
'}',
'',
'type Inline struct { Name string; Count int }',
'type Nested struct { Flags map[string]struct{}; Child struct { Value int } }',
// Shapes from worlpaker/go-syntax#25's test/semantic_tokens.go and test/stress.go.
'type OneLineFields struct{ A, B int; C chan<- struct{}; D []string; sync.Mutex }',
'type SplitOneLine struct { A int; B string',
'}',
'var stressFoo14 = struct{ Host string; Port int; Tags []string',
'}{}',
`type Many struct { ${Array.from({ length: 24 }, (_, index) => `F${index} int;`).join(' ')} Child struct { X int } }`,
// Last: its unclosed brace keeps the struct body open to the end of the source.
`type Open struct { ${'field '.repeat(48)}`,
].join('\n')
@@ -31,7 +31,7 @@ describe('gen-tool-catalog collectToolCatalog', () => {
'create_goal', 'create_worktree', 'edit', 'exit_plan_mode', 'get_goal', 'glob', 'grep',
'interrupt_agent', 'interrupt_agent', 'job_kill', 'job_list', 'job_output',
'list_agents', 'list_agents', 'list_mcp_resource_templates', 'list_mcp_resources',
'list_subagent_models', 'load_workspace_dependencies', 'lsp', 'plugin_manager', 'plugin_market', 'present', 'pwsh', 'pwsh', 'ralph',
'list_subagent_models', 'load_workspace_dependencies', 'lsp', 'plugin_manager', 'plugin_market', 'present', 'propose_action', 'pwsh', 'pwsh', 'ralph',
'read', 'read_image', 'read_mcp_resource', 'run_code', 'schedule_create', 'schedule_delete',
'schedule_list', 'schedule_update', 'send_message', 'send_message', 'session_event_read', 'session_event_search',
'session_event_trace', 'session_search', 'session_trace', 'skill', 'spawn_teammate',
+2 -2
View File
@@ -18,8 +18,8 @@
en: b55aeb927737b90d
zh: 0b215fcb5c2c7908
/packages-experimental/packages:
en: 9fd1c2ac5ede5d47
zh: 9e8fc6c374b6f5e9
en: 4d32c0a6c009a2ca
zh: 47bf9acd64100263
/packages-experimental/related-documentation:
en: 1cb9d3448d44e48d
zh: cd326d70a746b896
+6
View File
@@ -36,6 +36,12 @@ Promotion requires a named product role, documented public behavior and limitati
| Package | Role | ctx key |
|---|---|---|
| [`action-cards`](action-cards/README.md) | Optional action cards below answers: the proposal tool, card state, user actions and status notices | `ctx.actionCards` |
| [`client-ui-action-cards`](client-ui-action-cards/README.md) | Card frame below completed answers, kind bodies and the proposal process row | — |
| [`creation-recommendations`](creation-recommendations/README.md) | Explicit-need creation of persistent removable interface plugins | — |
| [`client-ui-creation-recommendations`](client-ui-creation-recommendations/README.md) | Personalized page card body with location selection and undo | — |
| [`plugin-recommendations`](plugin-recommendations/README.md) | Explicit-need official plugin recommendations in conversations | `ctx.pluginRecommendations` |
| [`client-ui-plugin-recommendations`](client-ui-plugin-recommendations/README.md) | Official plugin card body with catalog text and enabling | — |
| [`tool-session-query`](tool-session-query/README.md) | Workspace-authorized model session search, trace, and event reads | — |
| [`session-search`](session-search/README.md) | Optional experimental global session search tools | — |
| [`ralph-bundle`](ralph-bundle/README.md) | Optional repeated delegation with an isolated workflow engine | — |
+6
View File
@@ -36,6 +36,12 @@ kind: "package-group"
| 包 | 职责 | ctx 键 |
|---|---|---|
| [`action-cards`](action-cards/README.zh.md) | 回答下方的可选操作卡片:提议工具、卡片状态、用户操作与状态通知 | `ctx.actionCards` |
| [`client-ui-action-cards`](client-ui-action-cards/README.zh.md) | 已完成回答下方的卡片框架、种类正文与提议过程行 | — |
| [`creation-recommendations`](creation-recommendations/README.zh.md) | 针对明确需求创建持续可用、可移除的界面插件 | — |
| [`client-ui-creation-recommendations`](client-ui-creation-recommendations/README.zh.md) | 个性化页面卡片正文、位置选择和撤销 | — |
| [`plugin-recommendations`](plugin-recommendations/README.zh.md) | 在对话中针对明确需求推荐官方插件 | `ctx.pluginRecommendations` |
| [`client-ui-plugin-recommendations`](client-ui-plugin-recommendations/README.zh.md) | 官方插件卡片正文:目录文本与启用 | — |
| [`tool-session-query`](tool-session-query/README.zh.md) | 按工作区授权的模型会话搜索、追踪与事件读取 | — |
| [`session-search`](session-search/README.zh.md) | 可选实验性全局会话搜索工具 | — |
| [`ralph-bundle`](ralph-bundle/README.zh.md) | 带隔离 workflow 引擎的可选重复委派 | — |
@@ -0,0 +1,67 @@
# Bilingual-pair consistency record for README.md (docs/i18n/README.md): per heading
# section, a hash of its English and Chinese blocks outside code blocks and generated regions.
# After editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write packages/experimental/action-cards/README.md
/:
en: 1a68a7a733a2676d
zh: 01f72eb61a0626b7
/deepseek-ai-dsh-action-cards:
en: 63bf9077e164f6fe
zh: 8b696bb8c3d6fcd4
/deepseek-ai-dsh-action-cards/summary:
en: 7621088773d073f4
zh: 57674e6dcee15997
/deepseek-ai-dsh-action-cards/table-of-contents:
en: d152484eb41ac6b4
zh: b00dddf4c88f0a45
/deepseek-ai-dsh-action-cards/use-this-package:
en: 4e3a256d5aa6d29b
zh: b89ffa23ebb49e7e
/deepseek-ai-dsh-action-cards/use-this-package/register-a-card-kind:
en: 8432c02ef18eda11
zh: 6d43a0a32edd7e30
/deepseek-ai-dsh-action-cards/use-this-package/card-rules:
en: 2f5b3ccb91f3b30e
zh: 9bd877d9d92615da
/deepseek-ai-dsh-action-cards/understand-the-implementation:
en: d92243d71d2106c1
zh: 47f0840fa723bb31
/deepseek-ai-dsh-action-cards/further-exploration:
en: 1966ab8d7063c450
zh: 033c2c0d258a257f
/deepseek-ai-dsh-action-cards/model-experience:
en: 215e7ba838619b7b
zh: a311da3843709f90
/deepseek-ai-dsh-action-cards/model-experience/card-tool:
en: 4925bc5f7153825a
zh: 3a7bfb2210d9c6aa
/deepseek-ai-dsh-action-cards/model-experience/card-tool/what-the-model-sees:
en: 58521d3992ce42ec
zh: 45681f7b43e67613
/deepseek-ai-dsh-action-cards/model-experience/card-tool/token-effect:
en: 7efaaad09be98a07
zh: 86c7fa13d60c3588
/deepseek-ai-dsh-action-cards/model-experience/card-tool/kv-cache-effect:
en: d4192e35d47b5fa3
zh: c786828e123de657
/deepseek-ai-dsh-action-cards/model-experience/card-status-notice:
en: 407ebca5eb94992e
zh: 779819ea83974103
/deepseek-ai-dsh-action-cards/model-experience/card-status-notice/what-the-model-sees:
en: a14013ca3ddb5ee3
zh: 18cd7ffc62fd6590
/deepseek-ai-dsh-action-cards/model-experience/card-status-notice/what-the-model-sees/notice-preamble:
en: 04dd9469bfaff052
zh: 35110b231b8e3928
/deepseek-ai-dsh-action-cards/model-experience/card-status-notice/token-effect:
en: 4e73a2cff60b5e88
zh: 4887212815e7425d
/deepseek-ai-dsh-action-cards/model-experience/card-status-notice/kv-cache-effect:
en: 58785543e2428dba
zh: 4b082f77b3b2f482
/deepseek-ai-dsh-action-cards/known-limitations-and-deferred-work:
en: 163aa0c2a6b7e3f8
zh: 26cdcae33579114a
/deepseek-ai-dsh-action-cards/known-limitations-and-deferred-work/dev-note:
en: e67f633dc6fe7773
zh: bd8a8936229e035e
@@ -0,0 +1,138 @@
---
description: "Shared Host service of optional action cards: the propose_action tool, card state, user actions and status notices that experiment packages register card kinds into."
kind: "package-reference"
---
# @deepseek-ai/dsh-action-cards
English | [中文](README.zh.md)
## Summary
An experiment can offer one optional action below an answer, such as creating a page, without adding a tool of its own. The model loads the experiment's skill, then calls `propose_action`; the card appears after the turn ends, and nothing runs until the user acts on it. Each choice is recorded and reported to the model on its next request. The tool costs nothing until a card skill loads in the conversation.
## Table of Contents
- [Use this package](#use-this-package)
- [Understand the implementation](#understand-the-implementation)
- [Further Exploration](#further-exploration)
- [Model Experience](#model-experience)
- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
- [Dev Note](#dev-note)
-----
<a id="use-this-package"></a>
## Use this package
The [Personalized pages](../creation-recommendations/README.md) and [Plugin recommendations](../plugin-recommendations/README.md) bundles each insert this service and its [browser half](../client-ui-action-cards/README.md); enabling either bundle enables both. The service has one configuration field:
| Field | Default | Meaning |
|---|---|---|
| `queuedMessageLimit` | `2` | Queued next-turn user messages at or above which `propose_action` refuses the call, so no card is offered. |
The generated [configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-action-cards) is the exhaustive source for accepted fields.
### Register a card kind
A kind package injects `actionCards` and calls `ctx.actionCards.register(kind)` from its own plugin; unloading that plugin removes the kind, and actions on its cards are then refused as unavailable, except that Not now still dismisses an unstarted card. The kind names its action, the skill that proposes it, the closing invitation guidance that an accepted call's result gives the model, a zod schema for the card's `details`, the user actions it accepts and the shared checks each needs (`global` Remote access, `writes` refused in a read-only Session), the recorded phases that still offer dismissal, optional status guidance, the decision data fields the status notice reports, an optional `reconcile` that updates the card's recorded decision from state outside the Session before every action, and the handler for every action except `dismiss`. The handler records each new decision with `ctx.actionCards.record()`, which refuses an action no kind registered and the derived `replaced` phase, and returns `accepted`, `settled` with the decision in effect, `refused` or `unconfirmed`.
### Card rules
- The first accepted `propose_action` call of a turn becomes the card. The tool refuses a second call in the same turn, an unregistered action, a call whose kind skill was not loaded in the current turn, a blank title or brief, details that fail the kind schema, calls dispatched from code, calls from a subagent, and any call while `queuedMessageLimit` or more user messages wait for later turns.
- A newer card of any kind replaces every unstarted earlier card. Replacement is derived from the log and never recorded.
- Events a forked Session inherits create no cards.
- Before every action, including Not now, the kind's `reconcile` updates the card from state outside the Session. Not now is then recorded for an unstarted card and for the phases the kind lists; dismissed and replaced cards accept no further action. Without its registered kind, Not now still dismisses an unstarted card, and every other action is refused as unavailable.
- Concurrent actions on one card share one operation. Actions require the card's live root Agent.
-----
<a id="understand-the-implementation"></a>
## Understand the implementation
<details>
<summary>Implementation internals — click to expand</summary>
The package default-exports the `actionCards` Remote service. It registers `propose_action` once, globally, with a `when` condition: a live root Agent becomes eligible when its own direct `skill` call loads the skill of a registered kind, while loads by subagents or from code are ignored. The flag lives in memory until the Agent is disposed. The call's `execute` checks the live root, the queued messages the projection counts against `queuedMessageLimit` and the current turn's skill loads through the turn boundary, and keeps an in-memory mark of the turn's accepted call; the logged `tool/call` and its successful `tool/result` are the durable proposal.
The `actionCards` Session projection (state version 2) pairs each `propose_action` call with its successful result, records decisions from `plugin:action-cards/decision`, counts queued next-turn messages from inbox splices for the tool's limit, and keeps unreported decisions until a status notice whose `through` covers them enters the log. Its Client view `{ proposals, decisions }` is excluded from Session summaries.
The Remote `act(sessionId, callId, verb, payload?)` checks the owner, the live root and the card; Not now continues without a registered kind, while other verbs need the kind, the verb and Remote access. It then shares one operation per card. The kind's `reconcile` runs first and the live root is checked again; a dismissed or replaced card then settles unchanged, Not now is recorded, a writing verb is refused in a read-only Session, and every other verb goes to the kind handler under the Agent's initiator. `record()` also accepts an action whose kind has unregistered, because Cordis disposes a plugin's effects concurrently and an unloading kind still records the outcome of work it started. An `agent/pre-step` listener adds the status notice after later listeners prepared the step.
| Source | Contents |
|---|---|
| [`src/index.ts`](src/index.ts) | Service, tool, kind registry, Remote action and status notice |
| [`src/projection.ts`](src/projection.ts) | Session projection |
| [`src/types.ts`](src/types.ts) | Proposal, decision, view and result types |
| [`tests/`](tests/) | Activation, validation, projection, action and notice tests |
</details>
-----
<a id="further-exploration"></a>
## Further Exploration
- [Action card display](../client-ui-action-cards/README.md) — the browser frame, kind bodies and the process row.
- [Personalized pages](../creation-recommendations/README.md) and [Plugin recommendations](../plugin-recommendations/README.md) — the shipped card kinds.
- [Tools](../../core/tools/README.md) — conditional registration and step admission.
- [Session projection](../../session/session-projection/README.md) — the card state the browser reads.
<a id="model-experience"></a>
## Model Experience
### Card tool
#### What the model sees
The [`propose_action` declaration](../../../docs/tool-catalog.md#deepseek-aidsh-action-cards) appears only to a live root Agent whose own direct call loaded the skill of a registered kind in this Session, from the next step on, for that Agent's lifetime. Subagents never see it, and neither do PTC presets, whose skill loads are dispatched from code, so they show no cards. A kind's skill names the action and the fields of `details`. An accepted call returns `Card shown below your answer. Do not restate it. <invitation>`, where `<invitation>` is the kind's one- or two-sentence guidance on inviting the user to the card in the user's language, so the model writes an invitation only after a card exists. A refused call carries no invitation and returns its reason, for example `Load skill <skill> first, then call propose_action.`, `One card per turn; the earlier card stands.`, `The user has queued more messages; show no card and answer briefly.`, `Cards are offered in the main conversation only.`, `Unknown action "<action>". Registered actions: <actions>.`, where the list reads `none` once the last kind unregistered, or the failing `details` fields.
#### Token effect
Before a card skill loads, the tool adds no tokens. Afterwards each request of that Agent declares it: 574 characters of JSON, about 144 tokens at the token meter's four characters per token. A card costs one extra model round trip, the call before the final answer, and its tool result: a 49-character prefix plus the kind's invitation. The Personalized pages result is 305 characters, about 77 tokens at the same rate.
#### KV Cache effect
Activation is append-only on routes that declare tool updates, such as DeepSeek-V4.1-Flash: when the condition turns true the loop logs a header change and a `tool-addition` developer message, and those routes receive the definition as a deferred declaration referenced at that position, so the cached prefix is kept. A route without tool updates receives the tool in its declaration list from that step on, which changes the request prefix once. Disposing the Agent drops the activation; a reopened Session declares the tool again only after a card skill loads again, logging a removal and a later addition.
### Card status notice
#### What the model sees
After a decision is recorded, the next request carries one `action-cards` notice: the preamble below, the guidance sentences of each involved kind, `Continue with the user’s current request.`, and a JSON array of the changed cards with `action`, `callId`, `title`, `phase` and the data fields their kind reports.
##### Notice preamble
```markdown
Latest status of action cards from user actions and background work. These are status facts, not a request to start or retry work. Use these receipts instead of the earlier proposal when describing progress.
```
#### Token effect
One notice per request that follows new decisions, sized by the changed cards; unchanged cards add nothing, and several decisions of one card collapse to its latest.
#### KV Cache effect
The notice is appended after the request's other new context and never rewrites earlier messages. The notice neither starts a turn nor wakes an idle conversation.
## Known Limitations and Deferred Work
<a id="known-limitations-and-deferred-work"></a>
- Activation is per live Agent and kept in memory, so after a Session reopens the tool appears again only after a card skill loads again.
- PTC presets show no cards: skill loads and calls dispatched from code never activate or reach the tool, because the log records them as program dispatches rather than tool calls.
- Cards are offered in the main conversation only. A subagent's skill load, including a Creator child's, activates nothing and its calls are refused, because only a live root can act on a card.
- Only one card is offered per turn across all kinds. A later card of any kind replaces every unstarted earlier card, so a second kind's card replaces an unstarted card of the first.
- Refusal reasons of user actions are plain strings by design: the service defines `closing`, `not-live-root`, `unknown`, `unavailable`, `access-denied` and `read-only`, each kind adds its own, and a browser body names the reasons it knows and treats others as unavailable.
- Each kind validates `details` on both sides, with its Host schema when the model proposes and before each action, and with its browser body's own schema, so the two schemas change together.
- The Personalized pages and Plugin recommendations bundles both insert the rows of this service and its browser half, and with both enabled the Loader keeps one entry per row id. Two optional bundles cannot yet share a dependency row addressably: while both are enabled, the Plugins page cannot address the two shared rows (`action-cards`, `client-ui-action-cards`), so they cannot be switched or configured individually. Use the bundle switches; `queuedMessageLimit` can then be changed only in the profile's `cordis.patch.yml`.
<a id="dev-note"></a>
### Dev Note
<details>
<summary>Working context for maintainers — click to expand</summary>
None.
</details>
@@ -0,0 +1,138 @@
---
description: "可选操作卡片的共享 Host 服务:propose_action 工具、卡片状态、用户操作和状态通知,各实验包把自己的卡片种类注册进来。"
kind: "package-reference"
---
# @deepseek-ai/dsh-action-cards
[English](README.md) | 中文
## 概述
实验可以在回答下方提供一个可选操作,例如创建页面,而不必新增自己的工具。模型先加载该实验的 Skill,再调用 `propose_action`;本轮结束后卡片出现在回答下方,用户操作之前不会执行任何事。用户的每次选择都会被记录,并在模型的下一次请求中告知模型。对话中加载卡片 Skill 之前,这个工具不占用任何 token。
## 目录
- [使用本包](#use-this-package)
- [实现原理](#understand-the-implementation)
- [延伸阅读](#further-exploration)
- [模型体验](#model-experience)
- [已知限制与后续工作](#known-limitations-and-deferred-work)
- [开发备注](#dev-note)
-----
<a id="use-this-package"></a>
## 使用本包
[个性化页面](../creation-recommendations/README.zh.md)和[插件推荐](../plugin-recommendations/README.zh.md)两个 Bundle 都会插入本服务及其[浏览器端](../client-ui-action-cards/README.zh.md);启用其中任一个即同时启用两者。本服务只有一个配置字段:
| 字段 | 默认值 | 含义 |
|---|---|---|
| `queuedMessageLimit` | `2` | 排队到后续轮次的用户消息达到该数量时,`propose_action` 拒绝调用,因此不会展示卡片。 |
生成的[配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-action-cards)是可接受字段的完整来源。
### 注册卡片种类
种类所在的包注入 `actionCards`,并在自己的插件中调用 `ctx.actionCards.register(kind)`;该插件卸载时种类随之移除,此后对其卡片的操作会以 unavailable 拒绝,只有“暂不需要”仍可忽略尚未开始的卡片。种类需要给出:动作名、提议该动作的 Skill、被接受的调用结果交给模型的结尾邀请指引、卡片 `details` 的 zod 校验、接受的用户操作及各自需要的共享检查(`global` 表示需要全局 Remote 权限,`writes` 表示只读会话中拒绝)、仍可忽略卡片的已记录阶段、可选的状态说明、状态通知要报告的决定数据字段、可选的 `reconcile`(每次操作前根据会话之外的状态更新卡片的已记录决定),以及处理 `dismiss` 以外所有操作的处理器。处理器用 `ctx.actionCards.record()` 记录每个新决定(该方法拒绝没有种类注册过的动作和推导得出的 `replaced` 阶段),并返回 `accepted`、带当前决定的 `settled`、`refused` 或 `unconfirmed`。
### 卡片规则
- 一轮中第一个被接受的 `propose_action` 调用成为卡片。工具会拒绝同一轮的第二次调用、未注册的动作、本轮没有加载对应种类 Skill 的调用、空白的标题或说明、未通过种类校验的 details、从代码中发起的调用、来自 subagent 的调用,以及排队到后续轮次的用户消息达到 `queuedMessageLimit` 条时的任何调用。
- 任何种类的新卡片都会替换此前所有尚未开始的卡片。替换从日志推导,从不单独记录。
- fork 出的会话从父会话继承的事件不会产生卡片。
- 每次操作(包括“暂不需要”)之前,种类的 `reconcile` 先根据会话之外的状态更新卡片。之后,对尚未开始的卡片,以及处于种类列出阶段的卡片,“暂不需要”会被记录;已忽略、已替换的卡片不再接受任何操作。卡片的种类未注册时,“暂不需要”仍可忽略尚未开始的卡片,其他操作一律以 unavailable 拒绝。
- 同一张卡片上的并发操作共用一次处理。操作要求卡片所在会话的根 Agent 仍然在线。
-----
<a id="understand-the-implementation"></a>
## 实现原理
<details>
<summary>实现细节——点击展开</summary>
本包默认导出 `actionCards` Remote 服务。它在全局注册一次 `propose_action`,并附带 `when` 条件:在线的根 Agent 自己直接调用 `skill` 加载了已注册种类的 Skill 后,该 Agent 才满足条件,subagent 或代码中发起的加载一律忽略;这个标记保存在内存中,直到 Agent 被释放。调用的 `execute` 检查在线根 Agent、投影累计的排队消息是否达到 `queuedMessageLimit`,并通过轮次边界检查本轮加载过的 Skill,同时在内存中标记本轮已接受的调用;日志中的 `tool/call` 及其成功的 `tool/result` 就是持久的提议。
`actionCards` 会话投影(状态版本 2)把每个 `propose_action` 调用与其成功结果配对,从 `plugin:action-cards/decision` 读取决定,从收件箱拼接事件累计排队到下一轮的消息供工具上限使用,并保留尚未报告的决定,直到 `through` 覆盖它们的状态通知写入日志。投影的客户端视图 `{ proposals, decisions }` 不进入会话摘要。
Remote `act(sessionId, callId, verb, payload?)` 先检查服务所有者、在线根 Agent 和卡片;“暂不需要”在种类未注册时也继续处理,其他操作还需要种类、操作和 Remote 权限。然后同一张卡片共用一次处理。种类的 `reconcile` 先运行,之后再次检查在线根 Agent;已忽略或已替换的卡片原样返回,“暂不需要”被记录,写入类操作在只读会话中被拒绝,其余操作在该 Agent 的发起方作用域内交给种类处理器。`record()` 也接受种类已注销的动作,因为 Cordis 并发释放插件的各个 effect,卸载中的种类仍要记录已开始工作的结果。一个 `agent/pre-step` 监听器在后续监听器准备好本步之后追加状态通知。
| 源码 | 内容 |
|---|---|
| [`src/index.ts`](src/index.ts) | 服务、工具、种类注册表、Remote 操作与状态通知 |
| [`src/projection.ts`](src/projection.ts) | 会话投影 |
| [`src/types.ts`](src/types.ts) | 提议、决定、视图与结果类型 |
| [`tests/`](tests/) | 激活、校验、投影、操作与通知测试 |
</details>
-----
<a id="further-exploration"></a>
## 延伸阅读
- [操作卡片显示](../client-ui-action-cards/README.zh.md) — 浏览器端卡片框架、种类正文与过程行。
- [个性化页面](../creation-recommendations/README.zh.md)和[插件推荐](../plugin-recommendations/README.zh.md) — 随产品提供的卡片种类。
- [工具](../../core/tools/README.zh.md) — 条件注册与每步准入。
- [会话投影](../../session/session-projection/README.zh.md) — 浏览器读取的卡片状态。
<a id="model-experience"></a>
## 模型体验
### 卡片工具
#### 模型看到什么
[`propose_action` 声明](../../../docs/tool-catalog.zh.md#deepseek-aidsh-action-cards)只对在本会话中自己直接调用加载过已注册种类 Skill 的在线根 Agent 出现,从下一步开始,持续到该 Agent 被释放。subagent 看不到它;PTC 预设的 Skill 加载由代码发起,也看不到它,因此不会展示卡片。种类的 Skill 给出动作名和 `details` 的字段。被接受的调用返回 `Card shown below your answer. Do not restate it. <invitation>`,其中 `<invitation>` 是种类给出的一两句指引,说明如何用用户的语言邀请用户使用卡片,因此模型只会在卡片已经存在之后写邀请。被拒绝的调用不带邀请,只返回原因,例如 `Load skill <skill> first, then call propose_action.`、`One card per turn; the earlier card stands.`、`The user has queued more messages; show no card and answer briefly.`、`Cards are offered in the main conversation only.`、`Unknown action "<action>". Registered actions: <actions>.`(最后一个种类注销后列表为 `none`),或未通过校验的 `details` 字段。
#### Token 影响
加载卡片 Skill 之前,工具不增加任何 token。加载之后,该 Agent 的每次请求都会声明它:574 个字符的 JSON,按 token 计量器每 4 个字符计 1 个 token 约 144 个 token。出一张卡片多一次模型往返(最终回答前的这次调用)以及一条工具结果:49 个字符的前缀加上种类的邀请。个性化页面的结果为 305 个字符,按同样的比例约 77 个 token。
#### KV Cache 影响
在声明支持工具更新的路由上(例如 DeepSeek-V4.1-Flash),激活只追加内容:条件变为真时,循环记录一次请求头变化和一条 `tool-addition` 开发者消息,这些路由以延迟加载声明的形式在该位置引用新定义,因此已缓存的前缀保持不变。不支持工具更新的路由从这一步起在声明列表中收到该工具,请求前缀因此改变一次。Agent 被释放后激活随之失效;重新打开的会话要再次加载卡片 Skill 才会再次声明该工具,并依次记录一次移除和一次添加。
### 卡片状态通知
#### 模型看到什么
记录决定之后,下一次请求会带一条 `action-cards` 通知:下方的开头段落、所涉及各种类的说明句、`Continue with the user’s current request.`,以及一个 JSON 数组,列出发生变化的卡片的 `action`、`callId`、`title`、`phase` 和种类要报告的数据字段。
##### 通知开头
```markdown
Latest status of action cards from user actions and background work. These are status facts, not a request to start or retry work. Use these receipts instead of the earlier proposal when describing progress.
```
#### Token 影响
有新决定之后的每次请求带一条通知,大小取决于发生变化的卡片;没有变化的卡片不增加内容,同一张卡片的多次决定只报告最新一次。
#### KV Cache 影响
通知追加在本次请求其他新上下文之后,从不改写之前的消息。通知既不会开始新轮次,也不会唤醒空闲的对话。
## 已知限制与后续工作
<a id="known-limitations-and-deferred-work"></a>
- 激活按在线 Agent 记在内存中,因此会话重新打开后,要再次加载卡片 Skill 工具才会再次出现。
- PTC 预设不会展示卡片:从代码中发起的 Skill 加载和调用既不会激活工具,也到达不了工具,因为日志把它们记为程序派发而不是工具调用。
- 卡片只在主对话中提供。subagent(包括 Creator 子 Agent)加载 Skill 不会激活任何东西,其调用也会被拒绝,因为只有在线的根 Agent 才能对卡片进行操作。
- 所有种类合计每轮只展示一张卡片。任何种类的新卡片都会替换此前所有尚未开始的卡片,因此第二个种类的卡片会替换第一个种类尚未开始的卡片。
- 用户操作的拒绝原因有意设计为普通字符串:服务定义 `closing`、`not-live-root`、`unknown`、`unavailable`、`access-denied` 和 `read-only`,各种类可以添加自己的原因,浏览器端正文为已知原因显示文案,其余按 unavailable 处理。
- 每个种类在两端各自校验 `details`:Host 端的 schema 在模型提议时和每次操作前校验,浏览器端正文用自己的 schema 校验,因此两个 schema 要一起修改。
- 个性化页面和插件推荐两个 Bundle 都会插入本服务及其浏览器端的行,两个都启用时 Loader 对每个行 id 只保留一个条目。平台目前不支持两个可选 Bundle 以可定位的方式共享同一依赖行:两个都启用时,插件页无法定位这两条共享行(`action-cards`、`client-ui-action-cards`),不能单独开关或配置它们。请使用 Bundle 开关;此时 `queuedMessageLimit` 只能在 profile 的 `cordis.patch.yml` 中修改。
<a id="dev-note"></a>
### 开发备注
<details>
<summary>维护者工作背景——点击展开</summary>
无。
</details>
@@ -0,0 +1,6 @@
{
"meta": {
"title": "Aktionskarten",
"description": "Zeigt für aktivierte Experimente optionale Aktionskarten unter Antworten an und meldet die Entscheidungen des Benutzers an das Gespräch zurück."
}
}
@@ -0,0 +1,6 @@
{
"meta": {
"title": "Action cards",
"description": "Show optional action cards below answers for enabled experiments, and report the user's choices back to the conversation."
}
}
@@ -0,0 +1,6 @@
{
"meta": {
"title": "Tarjetas de acción",
"description": "Muestra tarjetas de acción opcionales debajo de las respuestas para los experimentos habilitados e informa a la conversación de las elecciones del usuario."
}
}
@@ -0,0 +1,6 @@
{
"meta": {
"title": "Cartes d'action",
"description": "Affiche des cartes d'action facultatives sous les réponses pour les expériences activées et transmet les choix de l'utilisateur à la conversation."
}
}
@@ -0,0 +1,6 @@
{
"meta": {
"title": "アクションカード",
"description": "有効な実験のために回答の下に任意のアクションカードを表示し、ユーザーの選択を会話に伝えます。"
}
}
@@ -0,0 +1,6 @@
{
"meta": {
"title": "Cartões de ação",
"description": "Mostra cartões de ação opcionais abaixo das respostas para experimentos ativados e informa as escolhas do usuário à conversa."
}
}
@@ -0,0 +1,6 @@
{
"meta": {
"title": "Карточки действий",
"description": "Показывает необязательные карточки действий под ответами для включенных экспериментов и передает выбор пользователя в разговор."
}
}
@@ -0,0 +1,6 @@
{
"meta": {
"title": "操作卡片",
"description": "为已启用的实验在回答下方显示可选操作卡片,并把用户的选择告知对话。"
}
}
@@ -0,0 +1,75 @@
{
"name": "@deepseek-ai/dsh-action-cards",
"description": "Optional action cards below answers: one shared proposal tool, card state, user actions and status notices",
"version": "0.2.1-rc.2",
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
"directory": "packages/experimental/action-cards"
},
"type": "module",
"main": "lib/index.js",
"types": "lib/types/index.d.ts",
"exports": {
".": {
"types": "./lib/types/index.d.ts",
"default": "./lib/index.js"
},
"./src/*": "./src/*",
"./package.json": "./package.json",
"./locale/*.json": "./locale/*.json",
"./types": {
"types": "./lib/types/types.d.ts",
"default": "./lib/types/types.js"
},
"./typert": {
"types": "./lib/typert.host.d.ts",
"default": "./lib/typert.host.js"
},
"./remote": {
"types": "./lib/typert.remote-client.d.ts",
"default": "./lib/typert.remote-client.js"
}
},
"license": "MIT",
"peerDependencies": {
"@deepseek-ai/cordis": "workspace:~",
"@deepseek-ai/dsh-typert-protocol": "workspace:*",
"@deepseek-ai/dsh-llm": "workspace:*",
"@deepseek-ai/dsh-tools": "workspace:*",
"@deepseek-ai/dsh-session": "workspace:*"
},
"devDependencies": {
"@deepseek-ai/cordis": "workspace:~",
"@deepseek-ai/dsh-typert-protocol": "workspace:*",
"@deepseek-ai/dsh-llm": "workspace:*",
"@deepseek-ai/dsh-tools": "workspace:*",
"@deepseek-ai/dsh-agent": "workspace:*",
"@deepseek-ai/dsh-agent-loop": "workspace:*",
"@deepseek-ai/dsh-agent-loop-testkit": "workspace:*",
"@deepseek-ai/dsh-session": "workspace:*",
"@deepseek-ai/dsh-session-projection": "workspace:*",
"@deepseek-ai/dsh-api-remote-access": "workspace:*",
"@deepseek-ai/dsh-sandbox-policy": "workspace:*",
"@deepseek-ai/dsh-skill": "workspace:*",
"@deepseek-ai/dsh-tool-skill": "workspace:*",
"@deepseek-ai/dsh-tool-testkit": "workspace:*",
"@deepseek-ai/dsh-util-values": "workspace:*"
},
"dependencies": {
"zod": "4.4.3"
},
"files": [
"locale/*.json",
"lib/index.js",
"lib/types/**/*.js",
"lib/types/**/*.d.ts",
"lib/typert.host.js",
"lib/typert.host.d.ts",
"lib/typert.remote-client.js",
"lib/typert.remote-client.d.ts"
]
}
@@ -0,0 +1,451 @@
/** Optional action cards below an answer: the `propose_action` tool, card state, user actions and status notices for registered kinds. */
import { FiberState, Service, type Context } from '@deepseek-ai/cordis'
import { z, type ZodError, type ZodType } from 'zod'
import { Remote, TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol'
import { createUserMessage, HarnessError, type ToolCallId } from '@deepseek-ai/dsh-llm'
import { defineTool, type ToolRunContext } from '@deepseek-ai/dsh-tools'
import { appendPluginRecord, SessionSeq, type Session } from '@deepseek-ai/dsh-session'
import type { Agent } from '@deepseek-ai/dsh-agent'
import type { ProjectionHandle } from '@deepseek-ai/dsh-session-projection'
import type { JsonValue } from '@deepseek-ai/dsh-util-values'
import type {} from '@deepseek-ai/dsh-api-remote-access'
import type {} from '@deepseek-ai/dsh-sandbox-policy'
import { actionCardProjection, PROPOSE_ACTION, type ActionCardState } from './projection.ts'
import type { ActionCardDecision, ActionCardFields, ActionCardProposal, ActionCardResult, ActionCardView } from './types.ts'
export type * from './types.ts'
/** Deployment-tunable limits for offering cards. */
export const Config = z.object({
/** Queued next-turn user messages at or above which `propose_action` refuses, so no card is offered. */
queuedMessageLimit: z.number().int().min(1).default(2),
}).prefault({})
/** Validated card limits after schema defaults. */
export type Config = z.infer<typeof Config>
declare module '@deepseek-ai/cordis' {
interface Context { actionCards: ActionCards }
}
/** The user action every card offers; the shared service records it. */
const DISMISS = 'dismiss'
/** Phases the projection derives from the log; no decision may record them. */
const DERIVED_PHASES: readonly string[] = ['replaced']
/** Phases after which no user action changes the card. */
const FINAL_PHASES: readonly string[] = ['dismissed', ...DERIVED_PHASES]
/** Tool whose successful loads activate `propose_action`; its `name` argument names the skill. */
const SKILL_TOOL = 'skill'
const skillArguments = z.object({ name: z.string() })
const STATUS_PREAMBLE = 'Latest status of action cards from user actions and background work. '
+ 'These are status facts, not a request to start or retry work. Use these receipts instead of the earlier proposal when describing progress.'
const STATUS_CLOSING = 'Continue with the user’s current request.'
/** The fiber fields the stopping check reads. */
export interface PluginFiber {
readonly uid: number | null
readonly state: FiberState
readonly parent: { readonly fiber: PluginFiber }
}
/**
* Whether a plugin must refuse new work: its fiber or an ancestor is disposed or unloading, since a parent's disposal
* begins before the plugin's own effects run, or the plugin's own drain has begun.
* @param owner - the plugin's fiber.
* @param draining - whether the plugin's drain effect has started.
* @returns whether the plugin is stopping.
*/
export function isPluginStopping(owner: PluginFiber, draining: boolean): boolean {
for (let fiber = owner; ; fiber = fiber.parent.fiber) {
if (fiber.uid === null || fiber.state === FiberState.UNLOADING) return true
if (fiber.parent.fiber === fiber) return draining
}
}
/** A refused `propose_action` call; the message tells the model what to do instead. */
export class ActionCardError extends HarnessError {}
/** Shared checks one user action of a kind requires before the kind handles it. */
export interface ActionCardVerb {
/** The connection must hold global Remote access, as for profile changes. */
readonly global?: true
/** Refused while the Session sandbox mode is read-only. */
readonly writes?: true
}
/** A proposal whose details passed its kind's schema. */
export type ActionCardProposalOf<Details> = Omit<ActionCardProposal, 'details'> & { readonly details: Details }
/** Host half of one action kind: the skill that proposes it, its fields and its user actions. */
export interface ActionCardKind<Details> {
/** Value of the `action` argument. */
readonly action: string
/** Skill whose instructions propose this action; a load in the current turn admits the call. */
readonly skill: string
/**
* Model-facing closing guidance that the result of an accepted call gives: one or two sentences on how to invite
* the user to the card, in the user's language. Refused calls never carry it, so the model invites the user only to a
* card that exists.
*/
readonly invitation: string
/** Validates `details` when the model proposes and before each user action. */
readonly details: ZodType<Details>
/** User actions besides `dismiss`, with the shared checks each requires; other verbs are refused as unavailable. */
readonly verbs: Readonly<Record<string, ActionCardVerb>>
/** Recorded phases that still offer the action; dismissal is recorded for these and for an unstarted card. */
readonly openPhases?: readonly string[]
/** Sentences the status notice adds when it reports this kind's decisions. */
readonly statusGuidance?: string
/** Decision data fields the status notice reports; omitted reports every field. */
readonly statusFields?: readonly string[]
/**
* Fields recorded with a dismissal.
* @param proposal - the dismissed card.
* @param payload - fields the user's client sent.
* @returns decision data, or undefined to record none.
*/
dismissal?(proposal: ActionCardProposalOf<Details>, payload: ActionCardFields | undefined): ActionCardFields | undefined
/**
* Bring the card's recorded decision up to date with state outside the Session, such as an installation that
* finished while the Session was closed, recording each change with {@link ActionCards.record}. The service awaits it
* before every user action, including `dismiss`, then reads the card's decision again, so a card that is no longer
* open settles instead of being dismissed.
* @param agent - live root of the card's Session.
* @param proposal - the card with validated details.
* @throws when that state cannot be read; the action is then refused as unavailable.
*/
reconcile?(agent: Agent, proposal: ActionCardProposalOf<Details>): Promise<void>
/**
* Handle one user action other than `dismiss` after the shared checks passed. The handler records each new
* decision with {@link ActionCards.record}.
* @param agent - live root of the card's Session.
* @param proposal - the card with validated details.
* @param verb - one of {@link verbs}.
* @param payload - fields the user's client sent; the kind validates them.
* @returns the action's result.
*/
act(agent: Agent, proposal: ActionCardProposalOf<Details>, verb: string, payload: ActionCardFields | undefined): Promise<ActionCardResult>
}
/** A registered kind with its details type erased behind validating closures. */
interface RegisteredKind {
readonly skill: string
readonly invitation: string
readonly verbs: Readonly<Record<string, ActionCardVerb>>
readonly openPhases: readonly string[]
readonly statusGuidance: string | undefined
readonly statusFields: readonly string[] | undefined
/** Describe why details fail the kind's schema; undefined when they pass. */
readonly detailsIssues: (details: ActionCardFields) => string | undefined
readonly dismissal: (proposal: ActionCardProposal, payload: ActionCardFields | undefined) => ActionCardFields | undefined
/** Absent when the kind keeps no state outside the Session. */
readonly reconcile: ((agent: Agent, proposal: ActionCardProposal) => Promise<void>) | undefined
readonly act: (
agent: Agent, proposal: ActionCardProposal, verb: string, payload: ActionCardFields | undefined,
) => Promise<ActionCardResult>
}
/** Own entries only: browser-supplied ids such as `toString` never reach inherited object members. */
function ownEntry<T>(record: Readonly<Record<string, T>>, key: string): T | undefined {
return Object.hasOwn(record, key) ? record[key] : undefined
}
function refused(reason: string): ActionCardResult {
return { status: 'refused', reason }
}
function issuesOf(error: ZodError): string {
return 'Invalid details: ' + error.issues.map(issue => `${['details', ...issue.path.map(String)].join('.')}: ${issue.message}`).join('; ')
}
/** Card tool, kind registry, Session projection, user actions and status notices. */
export class ActionCards extends TypertRemoteService {
static inject = ['tools', 'agents', 'sessionProjections', 'remoteAccess']
static Config = Config
private projection!: ProjectionHandle<ActionCardState>
private readonly kinds = new Map<string, RegisteredKind>()
/**
* Actions whose kind unregistered. Cordis disposes a plugin's effects concurrently, so a kind's unload drain can
* still record the outcome of its in-flight work after its registration is gone.
*/
private readonly retired = new Set<string>()
/** Skills of registered kinds each live agent has loaded; `propose_action` is declared while one is still registered. */
private readonly loaded = new Map<Agent, Set<string>>()
/** Kind skills each agent loaded in its latest turn. */
private readonly turnLoads = new Map<Agent, { readonly turn: number; readonly skills: Set<string> }>()
/** The call accepted in each agent's latest turn, until its result fails. */
private readonly accepted = new Map<Agent, { readonly turn: number; readonly callId: ToolCallId }>()
private readonly actions = new Map<string, Promise<ActionCardResult>>()
private closing = false
private readonly owner: Context['fiber']
/**
* @param ctx - Owning plugin context.
* @param config - Validated card limits; direct composition without a config gets the schema defaults.
*/
constructor(ctx: Context, private readonly config: Config = Config.parse({})) {
super(ctx, 'actionCards')
this.owner = ctx.fiber
}
protected async [Service.init](): Promise<void> {
this.projection = await this.ctx.sessionProjections.register(actionCardProjection)
this.ctx.tools.register(defineTool({
name: PROPOSE_ACTION,
description: 'Show the user one optional action card under this answer. Call only as the skill that names the action instructs.',
parameters: {
action: { type: 'string', required: true, description: 'Action name from the skill.' },
title: { type: 'string', required: true, description: 'Card title.' },
brief: { type: 'string', required: true, description: 'What the action does, in the user\'s language.' },
details: { type: 'object', additionalProperties: true, required: true, description: 'Fields the skill lists for this action.' },
},
output: {
schema: {
type: 'object', additionalProperties: false,
properties: {
action: { type: 'string', required: true }, title: { type: 'string', required: true }, invitation: { type: 'string', required: true },
},
},
render: (_args, value) => [{ type: 'text', text: `Card shown below your answer. Do not restate it. ${value.invitation}` }],
},
execute: (args, exec) => Promise.resolve(this.propose(args, exec)),
}), { when: ({ agent }) => this.declares(agent) })
this.ctx.on('tools/result', (exec, result) => {
const agent = exec.agent
if (agent === undefined) return
if (exec.name === PROPOSE_ACTION) {
if (result.isError && this.accepted.get(agent)?.callId === exec.callId) this.accepted.delete(agent)
return
}
// Loads by subagents or from code activate nothing: `propose_action` refuses both callers.
if (exec.parent !== undefined || !this.ctx.agents.roots().includes(agent)) return
const skill = exec.name === SKILL_TOOL && !result.isError ? skillArguments.safeParse(exec.arguments) : undefined
if (skill?.success !== true || ![...this.kinds.values()].some(kind => kind.skill === skill.data.name)) return
const turn = this.ctx.agents.turnBoundaryOf(agent.session).lastTurn
const loads = this.turnLoads.get(agent)
if (loads?.turn === turn) loads.skills.add(skill.data.name)
else this.turnLoads.set(agent, { turn, skills: new Set([skill.data.name]) })
this.loaded.set(agent, new Set([...this.loaded.get(agent) ?? [], skill.data.name]))
})
this.ctx.on('agent/disposed', ({ agent }) => {
this.loaded.delete(agent)
this.turnLoads.delete(agent)
this.accepted.delete(agent)
})
this.ctx.on('agent/pre-step', async ({ agent, signal }, next) => {
const decision = await next()
if (decision.kind === 'reject' || signal.aborted || this.isStopping()) return decision
const state = this.projection.stateOf(agent.session)
const pending = Object.values(state.pendingStatus)
if (pending.length === 0) return decision
const receipts = Object.values(state.view.decisions).filter(receipt => Object.hasOwn(state.pendingStatus, receipt.callId))
const guidance = new Set(receipts.flatMap(receipt => this.kinds.get(receipt.action)?.statusGuidance ?? []))
const updates = receipts.map((receipt) => {
const fields = this.kinds.get(receipt.action)?.statusFields
const data = receipt.data === undefined || fields === undefined ? receipt.data
: Object.fromEntries(Object.entries(receipt.data).filter(([key]) => fields.includes(key)))
return { action: receipt.action, callId: receipt.callId, title: ownEntry(state.view.proposals, receipt.callId)?.title,
phase: receipt.phase, ...data === undefined ? {} : { data } }
})
return { ...decision, messages: [...decision.messages, createUserMessage({
source: { kind: 'action-cards', through: SessionSeq(Math.max(...pending)), form: 'notice', summary: 'Action card status updated' },
content: [{ type: 'text', text: [STATUS_PREAMBLE, ...guidance, STATUS_CLOSING].join(' ') + '\n' + JSON.stringify(updates) }],
})] }
}, { prepend: true })
this.ctx.effect(() => async () => {
this.closing = true
await Promise.allSettled([...this.actions.values()])
}, 'action-cards: drain user actions')
}
/**
* Register one action kind for the caller's lifetime.
* @param kind - the kind's skill, details schema, verbs and handler.
* @returns disposer removing the kind; actions on its cards are then unavailable.
* @throws when another kind already registered the same action.
*/
register<Details>(kind: ActionCardKind<Details>): () => void {
const typed = (proposal: ActionCardProposal): ActionCardProposalOf<Details> | undefined => {
const details = kind.details.safeParse(proposal.details)
return details.success ? { ...proposal, details: details.data } : undefined
}
const reconcile = kind.reconcile?.bind(kind)
const entry: RegisteredKind = {
skill: kind.skill, invitation: kind.invitation, verbs: kind.verbs, openPhases: kind.openPhases ?? [],
statusGuidance: kind.statusGuidance, statusFields: kind.statusFields,
detailsIssues: (details) => {
const parsed = kind.details.safeParse(details)
return parsed.success ? undefined : issuesOf(parsed.error)
},
dismissal: (proposal, payload) => {
const card = typed(proposal)
return card === undefined ? undefined : kind.dismissal?.(card, payload)
},
// A card whose details no longer validate never reached the kind, so the kind has nothing to reconcile.
reconcile: reconcile === undefined ? undefined : async (agent, proposal) => {
const card = typed(proposal)
if (card !== undefined) await reconcile(agent, card)
},
act: (agent, proposal, verb, payload) => {
const card = typed(proposal)
return card === undefined ? Promise.resolve(refused('unavailable')) : kind.act(agent, card, verb, payload)
},
}
const dispose = this.ctx.effect(() => {
if (this.kinds.has(kind.action)) throw new Error(`action-cards: action "${kind.action}" is already registered`)
this.kinds.set(kind.action, entry)
return () => {
this.kinds.delete(kind.action)
this.retired.add(kind.action)
}
}, `action-cards: kind ${kind.action}`)
return () => { void dispose() }
}
/**
* Record a card decision in the Session log; the next model request reports it.
* @param agent - agent whose Session owns the card.
* @param decision - the card's new state.
* @throws when no kind ever registered the decision's action with this service, or its phase is one the projection derives.
*/
record(agent: Agent, decision: ActionCardDecision): void {
if (!this.kinds.has(decision.action) && !this.retired.has(decision.action)) {
throw new Error(`action-cards: cannot record a decision for unregistered action "${decision.action}"`)
}
if (DERIVED_PHASES.includes(decision.phase)) throw new Error(`action-cards: phase "${decision.phase}" is derived from the log and cannot be recorded`)
this.append(agent, decision)
}
/**
* Read the cards of one Session.
* @param session - a Session whose projections are prepared.
* @returns proposals and decisions by call id.
*/
view(session: Session): ActionCardView {
return this.projection.stateOf(session).view
}
/**
* Apply a user action to a logged card. Shared checks run first, then the kind reconciles the card; `dismiss` is
* recorded here and every other verb goes to the card's kind. `dismiss` also works on an unstarted card whose kind
* is no longer registered. Concurrent actions on one card share one operation.
* @param agent - live root resolved from the displayed Session.
* @param callId - the card's `propose_action` call id.
* @param verb - `dismiss` or one of the kind's verbs.
* @param payload - kind-defined fields such as a chosen location.
* @returns acceptance of background work, the decision in effect, or a refusal.
*/
@Remote
act(agent: Agent, callId: ToolCallId, verb: string, payload?: Record<string, JsonValue>): Promise<ActionCardResult> {
if (this.isStopping()) return Promise.resolve(refused('closing'))
if (!this.ctx.agents.roots().includes(agent)) return Promise.resolve(refused('not-live-root'))
const proposal = ownEntry(this.projection.stateOf(agent.session).view.proposals, callId)
if (proposal === undefined) return Promise.resolve(refused('unknown'))
const kind = this.kinds.get(proposal.action)
if (verb === DISMISS) return this.coalesce(agent, callId, () => this.dismiss(agent, kind, proposal, payload))
const policy = kind === undefined ? undefined : ownEntry(kind.verbs, verb)
if (kind === undefined || policy === undefined) return Promise.resolve(refused('unavailable'))
if (policy.global === true && !this.ctx.remoteAccess.allows({ kind: 'global' })) return Promise.resolve(refused('access-denied'))
return this.coalesce(agent, callId, () => this.perform(agent, kind, proposal, verb, policy, payload))
}
/** Share one operation per card, run under the Agent's initiator, until it settles. */
private coalesce(agent: Agent, callId: ToolCallId, run: () => Promise<ActionCardResult>): Promise<ActionCardResult> {
const key = JSON.stringify([agent.id, callId])
const existing = this.actions.get(key)
if (existing !== undefined) return existing
const operation = this.ctx.agents.withInitiator(agent, run).finally(() => { this.actions.delete(key) })
this.actions.set(key, operation)
return operation
}
/**
* Await the kind's reconciliation. A kind that unloads meanwhile still handles the action and refuses it itself.
* @returns a refusal when reconciliation failed or the root closed meanwhile; undefined to continue.
*/
private async reconciled(
agent: Agent, kind: RegisteredKind, proposal: ActionCardProposal, verb: string,
): Promise<ActionCardResult | undefined> {
if (kind.reconcile === undefined) return undefined
try { await kind.reconcile(agent, proposal) }
catch (error) {
this.ctx.logger.warn('Cannot reconcile action card %s before %s: %s', proposal.callId, verb, String(error))
return refused('unavailable')
}
return this.ctx.agents.roots().includes(agent) ? undefined : refused('not-live-root')
}
private async dismiss(
agent: Agent, kind: RegisteredKind | undefined, proposal: ActionCardProposal, payload: ActionCardFields | undefined,
): Promise<ActionCardResult> {
const refusal = kind === undefined ? undefined : await this.reconciled(agent, kind, proposal, DISMISS)
if (refusal !== undefined) return refusal
const decision = ownEntry(this.projection.stateOf(agent.session).view.decisions, proposal.callId)
// Without its kind no recorded phase is known to be open, and the kind may return to act on it.
if (decision !== undefined && (FINAL_PHASES.includes(decision.phase) || kind?.openPhases.includes(decision.phase) !== true)) {
return { status: 'settled', decision }
}
const data = kind?.dismissal(proposal, payload)
const dismissed: ActionCardDecision = { callId: proposal.callId, action: proposal.action, phase: 'dismissed', ...data === undefined ? {} : { data } }
this.append(agent, dismissed)
return { status: 'settled', decision: dismissed }
}
private async perform(
agent: Agent, kind: RegisteredKind, proposal: ActionCardProposal, verb: string, policy: ActionCardVerb,
payload: ActionCardFields | undefined,
): Promise<ActionCardResult> {
const refusal = await this.reconciled(agent, kind, proposal, verb)
if (refusal !== undefined) return refusal
const decision = ownEntry(this.projection.stateOf(agent.session).view.decisions, proposal.callId)
if (decision !== undefined && FINAL_PHASES.includes(decision.phase)) return { status: 'settled', decision }
if (policy.writes === true && this.ctx.get('sandboxPolicy')?.resolve({ session: agent.session }).mode === 'read-only') return refused('read-only')
return await kind.act(agent, proposal, verb, payload)
}
private append(agent: Agent, decision: ActionCardDecision): void {
appendPluginRecord(agent.session, 'plugin:action-cards/decision', decision)
}
/** Whether `propose_action` is declared for this agent's next step. */
private declares(agent: Agent): boolean {
const skills = this.loaded.get(agent)
return skills !== undefined && [...this.kinds.values()].some(kind => skills.has(kind.skill))
}
private propose(
args: { readonly action: string; readonly title: string; readonly brief: string; readonly details: ActionCardFields },
exec: ToolRunContext,
): { action: string; title: string; invitation: string } {
const agent = exec.agent
// Step admission declares the tool only to an agent; calls dispatched from code are not logged as tool calls,
// so no card could follow them.
if (agent === undefined || exec.parent !== undefined) throw new ActionCardError('Call propose_action directly, not from code.', 'ACTION_CARD_NESTED')
// Only a live root can act on a card, so a subagent's card could never be used.
if (!this.ctx.agents.roots().includes(agent)) throw new ActionCardError('Cards are offered in the main conversation only.', 'ACTION_CARD_ROOT')
if (this.projection.stateOf(agent.session).queued >= this.config.queuedMessageLimit) {
throw new ActionCardError('The user has queued more messages; show no card and answer briefly.', 'ACTION_CARD_QUEUED')
}
const kind = this.kinds.get(args.action)
// Admission is decided when the step starts, so the last kind can unregister before this call runs.
if (kind === undefined) throw new ActionCardError(`Unknown action "${args.action}". Registered actions: ${[...this.kinds.keys()].join(', ') || 'none'}.`, 'ACTION_CARD_UNKNOWN')
const turn = this.ctx.agents.turnBoundaryOf(agent.session).lastTurn
const loads = this.turnLoads.get(agent)
if (loads?.turn !== turn || !loads.skills.has(kind.skill)) {
throw new ActionCardError(`Load skill ${kind.skill} first, then call propose_action.`, 'ACTION_CARD_SKILL')
}
if (args.title.trim() === '' || args.brief.trim() === '') throw new ActionCardError('Write a title and a brief.', 'ACTION_CARD_BLANK')
const issues = kind.detailsIssues(args.details)
if (issues !== undefined) throw new ActionCardError(issues, 'ACTION_CARD_DETAILS')
if (this.accepted.get(agent)?.turn === turn || Object.values(this.view(agent.session).proposals).some(card => card.turn === turn)) {
throw new ActionCardError('One card per turn; the earlier card stands.', 'ACTION_CARD_LIMIT')
}
this.accepted.set(agent, { turn, callId: exec.callId })
return { action: args.action, title: args.title.trim(), invitation: kind.invitation }
}
private isStopping(): boolean {
return isPluginStopping(this.owner, this.closing)
}
}
export default ActionCards
@@ -0,0 +1,124 @@
/** Recover card proposals from logged `propose_action` calls and decisions from plugin records. */
import { z } from 'zod'
import { ToolCallId } from '@deepseek-ai/dsh-llm'
import { pluginRecordOf, SessionLogOffset } from '@deepseek-ai/dsh-session'
import type { ProjectionDefinition } from '@deepseek-ai/dsh-session-projection'
/** Model-facing name of the card tool. */
export const PROPOSE_ACTION = 'propose_action'
/** Kind-defined JSON object of a proposal or decision. */
export const fieldsSchema = z.record(z.string(), z.json())
/** Arguments of a `propose_action` call; blank titles and briefs create no card. */
export const argumentsSchema = z.object({
action: z.string().min(1),
title: z.string().trim().min(1),
brief: z.string().trim().min(1),
details: fieldsSchema,
})
const proposalSchema = z.object({
callId: z.string().min(1).transform(ToolCallId),
turn: z.number().int().nonnegative(),
action: z.string().min(1),
title: z.string().min(1),
brief: z.string().min(1),
details: fieldsSchema,
}).strict()
/** Recorded decision of one card. */
export const decisionSchema = z.object({
callId: z.string().min(1).transform(ToolCallId),
action: z.string().min(1),
phase: z.string().min(1),
data: fieldsSchema.optional(),
}).strict()
const viewSchema = z.object({
proposals: z.record(z.string(), proposalSchema),
decisions: z.record(z.string(), decisionSchema),
})
const stateSchema = z.object({
/** Fork-inherited prefix length; inherited events create no proposals or decisions. */
inheritedEventCount: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER).transform(SessionLogOffset),
view: viewSchema,
/** `propose_action` calls of the open turn still waiting for their result. */
calls: z.record(z.string(), proposalSchema.omit({ callId: true })),
/** Turn of the latest admitted proposal; later calls of that turn create no card. */
admittedTurn: z.number().int().nullable(),
/** Decisions not yet reported to the model, by call id, with the seq of their record. */
pendingStatus: z.record(z.string(), z.number()),
/** User messages still queued for later turns, folded from inbox splices; the card tool compares it with its limit. */
queued: z.number().int().nonnegative(),
})
/** Fold state; `view` is also the Client wire value. */
export type ActionCardState = z.infer<typeof stateSchema>
/** Type of {@link actionCardProjection}: the fold plus its Client view wire. */
export type ActionCardProjectionDefinition = Omit<ProjectionDefinition<'actionCards', ActionCardState>, 'wire'> & {
readonly wire: NonNullable<ProjectionDefinition<'actionCards', ActionCardState>['wire']>
}
/**
* Full-Session fold. A successful `propose_action` result admits the call as a card, at most one per turn;
* the first admitted call of a turn wins. A newer card of any kind replaces every unstarted earlier card.
* Events inherited by a forked Session create no proposals or decisions.
*/
export const actionCardProjection: ActionCardProjectionDefinition = {
key: 'actionCards', stateVersion: 2, stateSchema,
init: (_header, inheritedEventCount) => ({
inheritedEventCount, view: { proposals: {}, decisions: {} }, calls: {}, admittedTurn: null, pendingStatus: {}, queued: 0,
}),
apply: (state, event) => {
if (event.seq < state.inheritedEventCount) return state
switch (event.type) {
case 'turn/start':
// A turn's calls settle inside it; a call left without a result never becomes a card.
return Object.keys(state.calls).length === 0 ? state : { ...state, calls: {} }
case 'agent/inbox/spliced':
if (event.data.target !== 'next-turn') return state
return { ...state, queued: Math.max(0, state.queued - (event.data.removedCount ?? 0) + event.data.inserted.length) }
case 'user/message': {
if (event.data.source.kind !== 'action-cards') return state
const through = event.data.source.through
return { ...state, pendingStatus: Object.fromEntries(Object.entries(state.pendingStatus).filter(([, seq]) => seq > through)) }
}
case 'tool/call': {
if (event.data.name !== PROPOSE_ACTION) return state
let raw: unknown
try { raw = JSON.parse(event.data.arguments) }
catch { return state } // Malformed model JSON failed in the tool too and creates no card.
const parsed = argumentsSchema.safeParse(raw)
if (!parsed.success) return state
return { ...state, calls: { ...state.calls, [event.data.callId]: { turn: event.data.turn, ...parsed.data } } }
}
case 'tool/result': {
const callId = event.data.message.toolCallId
const call = Object.hasOwn(state.calls, callId) ? state.calls[callId] : undefined
if (call === undefined) return state
const calls = Object.fromEntries(Object.entries(state.calls).filter(([id]) => id !== callId))
if (event.data.message.isError === true || state.admittedTurn === call.turn) return { ...state, calls }
const decisions = { ...state.view.decisions }
// A newer card supersedes every unstarted card; the model made that choice, so no status is reported.
for (const earlier of Object.values(state.view.proposals)) {
if (decisions[earlier.callId] === undefined) decisions[earlier.callId] = { callId: earlier.callId, action: earlier.action, phase: 'replaced' }
}
return { ...state, calls, admittedTurn: call.turn,
view: { proposals: { ...state.view.proposals, [callId]: { callId, ...call } }, decisions } }
}
default: {
const record = pluginRecordOf(event)
if (record?.type !== 'plugin:action-cards/decision') return state
// A malformed or newer record is skipped; throwing here would fail every later read of this Session.
const decision = decisionSchema.safeParse(record.data)
if (!decision.success) return state
return { ...state, view: { ...state.view, decisions: { ...state.view.decisions, [decision.data.callId]: decision.data } },
pendingStatus: { ...state.pendingStatus, [decision.data.callId]: event.seq } }
}
}
},
wire: { summary: false, viewSchema, view: (state: ActionCardState) => state.view },
}
@@ -0,0 +1,79 @@
/** Card proposals, recorded decisions and user-action results shared by every action kind. */
import type { ContextFormed } from '@deepseek-ai/dsh-llm'
import type { ToolCallId } from '@deepseek-ai/dsh-llm/brand'
import type { SessionSeq } from '@deepseek-ai/dsh-session/types'
import type { SessionProjectionSummaryExcluded } from '@deepseek-ai/dsh-session-projection/types'
import type { JsonValue } from '@deepseek-ai/dsh-util-values'
/** Kind-defined JSON fields of a proposal or decision; each kind validates its own fields. */
export type ActionCardFields = Readonly<Record<string, JsonValue>>
/** One card admitted from a successful `propose_action` call. */
export interface ActionCardProposal {
/** Id of the logged tool call. */
readonly callId: ToolCallId
/** Turn whose answer the card follows. */
readonly turn: number
/** Registered action kind named by the call. */
readonly action: string
readonly title: string
/** What the action does, in the user's language. */
readonly brief: string
/** Fields the kind's skill asks for. */
readonly details: ActionCardFields
}
/**
* Latest state of one card. Kinds define their own phases; `dismissed` is shared.
* `replaced` (a later proposal superseded an unstarted card) is derived from the
* log and never recorded.
*/
export interface ActionCardDecision {
readonly callId: ToolCallId
readonly action: string
readonly phase: string
/** Kind-defined fields such as a chosen location or a created package. */
readonly data?: ActionCardFields | undefined
}
/** Open-Session proposals and decisions by call id, excluded from Session summaries. */
export interface ActionCardView {
readonly proposals: Readonly<Record<string, ActionCardProposal>>
readonly decisions: Readonly<Record<string, ActionCardDecision>>
}
/**
* Result of a user action. `accepted` starts background work that records its
* own progress; `settled` returns the decision now in effect; `refused` changed
* nothing; `unconfirmed` lost its outcome to an unloading owner, which the
* kind reconciles when the Session reopens.
*/
export type ActionCardResult =
| { readonly status: 'accepted' }
| { readonly status: 'settled'; readonly decision: ActionCardDecision }
| { readonly status: 'refused'; readonly reason: string }
| { readonly status: 'unconfirmed'; readonly reason: string }
declare module '@deepseek-ai/dsh-llm' {
interface MessageSourceMap {
/** Card status notice; other readers preserve the recorded text without this plugin.
* The producer uses through only to acknowledge delivered decisions.
* @persistenceAttribution
*/
'action-cards': { kind: 'action-cards'; through: SessionSeq } & ContextFormed
}
}
declare module '@deepseek-ai/dsh-session/types' {
interface PluginRecordMap {
/** Dismissals and kind-defined progress of action cards. */
'plugin:action-cards/decision': ActionCardDecision
}
}
declare module '@deepseek-ai/dsh-session-projection/types' {
interface SessionProjectionMap {
/** Action card proposals and decisions for the currently open Session. */
actionCards: SessionProjectionSummaryExcluded<ActionCardView>
}
}
@@ -0,0 +1,11 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`reports recorded decisions once on the next request, with each kind guidance once, without waking the conversation > status notice 1`] = `
[
{
"text": "Latest status of action cards from user actions and background work. These are status facts, not a request to start or retry work. Use these receipts instead of the earlier proposal when describing progress. Failed runs can be retried from their card. Continue with the user’s current request.
[{"action":"test_action","callId":"call-card-1","title":"Counter","phase":"failed","data":{"reason":"timeout"}},{"action":"test_action","callId":"call-card-2","title":"Second","phase":"dismissed"},{"action":"retired_action","callId":"call-orphan","phase":"done"}]",
"type": "text",
},
]
`;
@@ -0,0 +1,36 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`declares propose_action from the step after a card skill loads, and logs the addition > tool declaration 1`] = `
{
"description": "Show the user one optional action card under this answer. Call only as the skill that names the action instructs.",
"name": "propose_action",
"parameters": {
"properties": {
"action": {
"description": "Action name from the skill.",
"type": "string",
},
"brief": {
"description": "What the action does, in the user's language.",
"type": "string",
},
"details": {
"additionalProperties": true,
"description": "Fields the skill lists for this action.",
"type": "object",
},
"title": {
"description": "Card title.",
"type": "string",
},
},
"required": [
"action",
"title",
"brief",
"details",
],
"type": "object",
},
}
`;
@@ -0,0 +1,167 @@
import { expect, it, onTestFinished, vi } from 'vitest'
import { z } from 'zod'
import { ToolCallId } from '@deepseek-ai/dsh-llm'
import type { Agent } from '@deepseek-ai/dsh-agent'
import type { ActionCardProposalOf, ActionCardResult } from '../src/index.ts'
import { ACTION, callId, fixture, proposal, type TestDetails } from './fixture.ts'
const dismissed = (data?: object) => ({ status: 'settled', decision: { callId, action: ACTION, phase: 'dismissed', ...data === undefined ? {} : { data } } })
it('refuses unknown cards, closed owners, verbs of removed kinds and undeclared verbs before any handler runs', async () => {
const f = await fixture()
expect(await f.ctx.actionCards.act(f.agent, callId, 'start')).toEqual({ status: 'refused', reason: 'unknown' })
f.propose()
for (const id of ['toString', 'constructor']) {
expect(await f.ctx.actionCards.act(f.agent, ToolCallId(id), 'dismiss')).toEqual({ status: 'refused', reason: 'unknown' })
}
for (const verb of ['toString', 'missing']) {
expect(await f.ctx.actionCards.act(f.agent, callId, verb)).toEqual({ status: 'refused', reason: 'unavailable' })
}
const roots = vi.spyOn(f.ctx.agents, 'roots').mockReturnValue([])
expect(await f.ctx.actionCards.act(f.agent, callId, 'start')).toEqual({ status: 'refused', reason: 'not-live-root' })
roots.mockRestore()
await f.kindFiber.dispose()
expect(await f.ctx.actionCards.act(f.agent, callId, 'start')).toEqual({ status: 'refused', reason: 'unavailable' })
const service = f.ctx.actionCards
await f.cards.dispose()
expect(await service.act(f.agent, callId, 'dismiss')).toEqual({ status: 'refused', reason: 'closing' })
expect(f.act).not.toHaveBeenCalled()
})
it('applies the shared access and read-only checks the kind declares, then hands typed details to the kind', async () => {
const f = await fixture()
f.propose()
const access = vi.spyOn(f.ctx.remoteAccess, 'allows').mockReturnValue(false)
expect(await f.ctx.actionCards.act(f.agent, callId, 'publish')).toEqual({ status: 'refused', reason: 'access-denied' })
expect(await f.ctx.actionCards.act(f.agent, callId, 'start', { note: 'x' })).toEqual({ status: 'settled', decision: { callId, action: ACTION, phase: 'start' } })
access.mockRestore()
expect(f.act).toHaveBeenCalledWith(f.agent, { callId, turn: 1, ...proposal }, 'start', { note: 'x' })
const resolve = vi.fn(() => ({ mode: 'read-only', workspaceRoot: '/' }))
f.ctx.provide('sandboxPolicy', { resolve })
expect(await f.ctx.actionCards.act(f.agent, callId, 'write')).toEqual({ status: 'refused', reason: 'read-only' })
expect(resolve).toHaveBeenCalledWith({ session: f.agent.session })
expect((await f.ctx.actionCards.act(f.agent, callId, 'publish')).status).toBe('settled')
resolve.mockReturnValue({ mode: 'workspace-write', workspaceRoot: '/' })
expect((await f.ctx.actionCards.act(f.agent, callId, 'write')).status).toBe('settled')
f.propose(ToolCallId('call-invalid'), { ...proposal, details: { value: 3 } }, f.agent, 2)
expect(await f.ctx.actionCards.act(f.agent, ToolCallId('call-invalid'), 'start')).toEqual({ status: 'refused', reason: 'unavailable' })
expect(f.act).toHaveBeenCalledTimes(3)
})
it('records dismissal of an unstarted or open card with the kind data, and settles started or final cards', async () => {
const f = await fixture({
openPhases: ['failed'],
dismissal: (card, payload) => ({ value: card.details.value, ...z.object({ choice: z.string() }).safeParse(payload).data }),
})
f.propose()
expect(await f.ctx.actionCards.act(f.agent, callId, 'dismiss', { choice: 'later' })).toEqual(dismissed({ value: 'a', choice: 'later' }))
expect(f.view().decisions[callId]?.phase).toBe('dismissed')
for (const verb of ['dismiss', 'start']) expect(await f.ctx.actionCards.act(f.agent, callId, verb)).toEqual(dismissed({ value: 'a', choice: 'later' }))
f.ctx.actionCards.record(f.agent, { callId, action: ACTION, phase: 'failed' })
expect(await f.ctx.actionCards.act(f.agent, callId, 'dismiss')).toEqual(dismissed({ value: 'a' }))
f.ctx.actionCards.record(f.agent, { callId, action: ACTION, phase: 'running', data: { step: 1 } })
expect(await f.ctx.actionCards.act(f.agent, callId, 'dismiss')).toEqual({ status: 'settled',
decision: { callId, action: ACTION, phase: 'running', data: { step: 1 } } })
f.propose(ToolCallId('call-invalid'), { ...proposal, details: { value: 3 } }, f.agent, 2)
expect(await f.ctx.actionCards.act(f.agent, ToolCallId('call-invalid'), 'dismiss')).toEqual({ status: 'settled',
decision: { callId: 'call-invalid', action: ACTION, phase: 'dismissed' } })
expect(f.act).not.toHaveBeenCalled()
})
it('dismisses without data when the kind records none', async () => {
const f = await fixture()
f.propose()
expect(await f.ctx.actionCards.act(f.agent, callId, 'dismiss', { choice: 'later' })).toEqual(dismissed())
})
it('shares one operation between concurrent actions on a card and drains it on unload', async () => {
const pending = Promise.withResolvers<ActionCardResult>()
onTestFinished(() => { pending.resolve({ status: 'accepted' }) })
const f = await fixture({ act: vi.fn(() => pending.promise) })
f.propose()
const first = f.ctx.actionCards.act(f.agent, callId, 'start')
expect(f.ctx.actionCards.act(f.agent, callId, 'dismiss')).toBe(first)
const disposing = f.cards.dispose()
pending.resolve({ status: 'accepted' })
expect(await first).toEqual({ status: 'accepted' })
await disposing
})
it('rejects a second kind for the same action and releases a kind through its disposer', async () => {
const f = await fixture()
const kind = (action: string) => ({ action, skill: 'other-skill', invitation: 'Invite the user.', details: z.object({}), verbs: { start: {} },
act: () => Promise.resolve<ActionCardResult>({ status: 'accepted' }) })
expect(() => f.ctx.actionCards.register(kind(ACTION))).toThrow('action-cards: action "test_action" is already registered')
const unregister = f.ctx.actionCards.register(kind('other_action'))
f.propose(ToolCallId('call-other'), { action: 'other_action', title: 'Other', brief: 'Other card.', details: {} })
expect(await f.ctx.actionCards.act(f.agent, ToolCallId('call-other'), 'start')).toEqual({ status: 'accepted' })
unregister()
expect(await f.ctx.actionCards.act(f.agent, ToolCallId('call-other'), 'start')).toEqual({ status: 'refused', reason: 'unavailable' })
})
it('lets the kind reconcile before every action, so a card that is no longer open settles instead of being dismissed', async () => {
let finished = false
const reconcile = vi.fn(async (agent: Agent, card: ActionCardProposalOf<TestDetails>) => {
if (finished) f.ctx.actionCards.record(agent, { callId: card.callId, action: ACTION, phase: 'installed' })
})
const f = await fixture({ openPhases: ['unconfirmed'], reconcile })
f.propose()
f.ctx.actionCards.record(f.agent, { callId, action: ACTION, phase: 'unconfirmed' })
finished = true
const installed = { status: 'settled', decision: { callId, action: ACTION, phase: 'installed' } }
expect(await f.ctx.actionCards.act(f.agent, callId, 'dismiss')).toEqual(installed)
expect(reconcile).toHaveBeenCalledWith(f.agent, { callId, turn: 1, ...proposal })
expect(f.view().decisions[callId]?.phase).toBe('installed')
expect(await f.ctx.actionCards.act(f.agent, callId, 'start')).toEqual({ status: 'settled', decision: { callId, action: ACTION, phase: 'start' } })
expect(reconcile).toHaveBeenCalledTimes(2)
f.propose(ToolCallId('call-invalid'), { ...proposal, details: { value: 3 } }, f.agent, 2)
expect((await f.ctx.actionCards.act(f.agent, ToolCallId('call-invalid'), 'dismiss')).status).toBe('settled')
expect(reconcile).toHaveBeenCalledTimes(2)
})
it('refuses the action when reconciliation fails or the root closes meanwhile', async () => {
const reconcile = vi.fn<(agent: Agent) => Promise<void>>(async () => { throw new Error('profile read failed') })
const f = await fixture({ reconcile })
f.propose()
const warn = vi.spyOn(f.ctx.logger, 'warn').mockImplementation(() => {})
expect(await f.ctx.actionCards.act(f.agent, callId, 'dismiss')).toEqual({ status: 'refused', reason: 'unavailable' })
expect(warn).toHaveBeenCalledWith('Cannot reconcile action card %s before %s: %s', callId, 'dismiss', 'Error: profile read failed')
expect(await f.ctx.actionCards.act(f.agent, callId, 'start')).toEqual({ status: 'refused', reason: 'unavailable' })
expect(warn).toHaveBeenLastCalledWith('Cannot reconcile action card %s before %s: %s', callId, 'start', 'Error: profile read failed')
expect(f.view().decisions).toEqual({})
reconcile.mockImplementationOnce(async () => { await f.agentHandle.dispose() })
expect(await f.ctx.actionCards.act(f.agent, callId, 'dismiss')).toEqual({ status: 'refused', reason: 'not-live-root' })
expect(f.act).not.toHaveBeenCalled()
})
it('refuses to record decisions of actions no kind registered or of phases the projection derives', async () => {
const f = await fixture()
f.propose()
expect(() => { f.ctx.actionCards.record(f.agent, { callId, action: 'other_action', phase: 'done' }) })
.toThrow('action-cards: cannot record a decision for unregistered action "other_action"')
expect(() => { f.ctx.actionCards.record(f.agent, { callId, action: ACTION, phase: 'replaced' }) })
.toThrow('action-cards: phase "replaced" is derived from the log and cannot be recorded')
expect(f.view().decisions).toEqual({})
// An unloading kind still records the outcome of work it had started.
await f.kindFiber.dispose()
f.ctx.actionCards.record(f.agent, { callId, action: ACTION, phase: 'interrupted' })
expect(f.view().decisions[callId]?.phase).toBe('interrupted')
})
it('dismisses an unstarted card whose kind is no longer registered, without data, and settles recorded ones', async () => {
const f = await fixture({ openPhases: ['failed'], dismissal: () => ({ value: 'kind data' }) })
f.propose(ToolCallId('call-failed'), proposal, f.agent, 1)
f.ctx.actionCards.record(f.agent, { callId: ToolCallId('call-failed'), action: ACTION, phase: 'failed' })
f.propose(ToolCallId('call-done'), proposal, f.agent, 2)
f.ctx.actionCards.record(f.agent, { callId: ToolCallId('call-done'), action: ACTION, phase: 'dismissed' })
f.propose(callId, proposal, f.agent, 3)
await f.kindFiber.dispose()
expect(await f.ctx.actionCards.act(f.agent, callId, 'dismiss', { choice: 'later' })).toEqual(dismissed())
expect(f.view().decisions[callId]).toEqual({ callId, action: ACTION, phase: 'dismissed' })
// The kind may return and act on its recorded phases, so they stay as they are.
expect(await f.ctx.actionCards.act(f.agent, ToolCallId('call-failed'), 'dismiss')).toEqual({ status: 'settled',
decision: { callId: 'call-failed', action: ACTION, phase: 'failed' } })
expect(await f.ctx.actionCards.act(f.agent, ToolCallId('call-done'), 'dismiss')).toEqual({ status: 'settled',
decision: { callId: 'call-done', action: ACTION, phase: 'dismissed' } })
expect(f.act).not.toHaveBeenCalled()
})
@@ -0,0 +1,73 @@
/** Real Agent, Tools, skills and projections with the card service and one scripted test kind. */
import { Context } from '@deepseek-ai/cordis'
import { onTestFinished, vi } from 'vitest'
import { z } from 'zod'
import { createToolResultMessage, ToolCallId } from '@deepseek-ai/dsh-llm'
import { SessionId, type SessionEvent } from '@deepseek-ai/dsh-session'
import { mountAgentLoopTestDependencies, mountAgentLoopTestHarness } from '@deepseek-ai/dsh-agent-loop-testkit'
import Skills, { BUNDLED_SKILL_RANK } from '@deepseek-ai/dsh-skill'
import * as SkillTool from '@deepseek-ai/dsh-tool-skill'
import RemoteAccess from '@deepseek-ai/dsh-api-remote-access'
import type { Agent } from '@deepseek-ai/dsh-agent'
import ActionCards from '../src/index.ts'
import type { ActionCardFields, ActionCardKind, ActionCardProposalOf, ActionCardResult } from '../src/index.ts'
export const ACTION = 'test_action'
export const SKILL = 'test-skill'
export const INVITATION = 'Invite the user to try the card in one sentence.'
export const callId = ToolCallId('call-card-1')
export const proposal = { action: ACTION, title: 'Counter', brief: 'Keep a counter visible.', details: { value: 'a' } }
/** Details of the test kind. */
export interface TestDetails { readonly value: string }
/** Provide one model-invocable skill per name. */
export function provideSkills(ctx: Context, ...names: string[]): void {
ctx.skills.registerProvider(() => ({
name: 'card-skills',
list: () => Promise.resolve(names.map(name => ({
name, description: `Use ${name}.`, provider: 'card-skills', source: 'bundled' as const, rank: BUNDLED_SKILL_RANK, locator: null,
invocation: { modelInvocable: true, userInvocable: false },
}))),
get: candidate => Promise.resolve({ name: candidate.name, description: candidate.description, provider: 'card-skills', source: 'bundled' as const,
invocation: candidate.invocation, content: `Call propose_action for ${candidate.name}.` }),
}))
}
/** Mount the card service with one registered kind whose handler is a spy. */
export async function fixture(kind: Partial<ActionCardKind<TestDetails>> = {}) {
const ctx = new Context()
onTestFinished(async () => { await ctx.fiber.dispose() })
await mountAgentLoopTestDependencies(ctx, { workingDirectory: true })
const harness = await mountAgentLoopTestHarness(ctx)
await ctx.plugin(RemoteAccess)
await ctx.plugin(Skills)
await ctx.plugin(SkillTool)
provideSkills(ctx, SKILL, 'other-skill')
const cards = await ctx.plugin(ActionCards)
type Handler = (
agent: Agent, card: ActionCardProposalOf<TestDetails>, verb: string, payload: ActionCardFields | undefined,
) => Promise<ActionCardResult>
const act = vi.fn<Handler>(async (_agent, card, verb) =>
({ status: 'settled', decision: { callId: card.callId, action: ACTION, phase: verb } }))
const kindFiber = await ctx.plugin({ name: 'test-kind', inject: ['actionCards'], apply: (inner: Context) => {
inner.actionCards.register({
action: ACTION, skill: SKILL, invitation: INVITATION, details: z.object({ value: z.string() }),
verbs: { start: {}, publish: { global: true }, write: { writes: true } },
act, ...kind,
})
} })
const agentHandle = await ctx.agents.create({ sessionId: SessionId('cards-root') })
const agent = agentHandle.agent
const events: SessionEvent[] = []
ctx.on('session/event', (session, event) => { if (session === agent.session) events.push(event) })
let step = 0
/** Log a successful `propose_action` call, as the loop does after the tool accepts it. */
const propose = (id: ToolCallId = callId, args: object = proposal, caller = agent, turn = 1) => {
caller.session.append('tool/call', { turn, step: ++step, callId: id, name: 'propose_action', arguments: JSON.stringify(args) })
caller.session.append('tool/result', { turn, step, message: createToolResultMessage({ callId: id, content: [], isError: false }) }, { surfaceOp: 'append' })
return id
}
const view = (caller = agent) => ctx.actionCards.view(caller.session)
return { ctx, harness, cards, kindFiber, agent, agentHandle, events, act, propose, view }
}
@@ -0,0 +1,130 @@
import { expect, it } from 'vitest'
import { createToolResultMessage, createUserMessage, ToolCallId } from '@deepseek-ai/dsh-llm'
import {
appendPluginRecord, Session, SESSION_FORMAT_VERSION, SessionId, SessionLogOffset, SessionSeq, type SessionEvent, type SessionHeader,
} from '@deepseek-ai/dsh-session'
import { actionCardProjection, type ActionCardState } from '../src/projection.ts'
import type { ActionCardDecision } from '../src/types.ts'
const projection = actionCardProjection
const args = { action: 'test_action', title: 'Counter', brief: 'Keep count visible', details: { value: 'a' } }
let seq = 0
function init(inherited = 0): ActionCardState {
const header: SessionHeader = { version: SESSION_FORMAT_VERSION, id: SessionId('cards'), createdAt: 0, isSeeded: inherited > 0 }
return projection.init(header, SessionLogOffset(inherited))
}
function turn(value: number): SessionEvent<'turn/start'> {
return { type: 'turn/start', seq: SessionSeq(seq++), time: 0, data: { turn: value } }
}
function call(callId: string, value: object | string = args, turnNumber = 1, name = 'propose_action'): SessionEvent<'tool/call'> {
return { type: 'tool/call', seq: SessionSeq(seq++), time: 0,
data: { turn: turnNumber, step: 1, callId: ToolCallId(callId), name, arguments: typeof value === 'string' ? value : JSON.stringify(value) } }
}
function result(callId: string, isError = false, turnNumber = 1): SessionEvent<'tool/result'> {
return { type: 'tool/result', seq: SessionSeq(seq++), time: 0, surfaceOp: 'append',
data: { turn: turnNumber, step: 1, message: createToolResultMessage({ callId: ToolCallId(callId), content: [], isError }) } }
}
function spliced(inserted: number, removedCount?: number, target: 'next-turn' | 'next-step' = 'next-turn'): SessionEvent<'agent/inbox/spliced'> {
return { type: 'agent/inbox/spliced', seq: SessionSeq(seq++), time: 0, data: { target, start: 0, ...removedCount === undefined ? {} : { removedCount },
inserted: Array.from({ length: inserted }, () => createUserMessage({ content: [{ type: 'text', text: 'next' }], source: { kind: 'user' } })) } }
}
function records(...data: ActionCardDecision[]): readonly SessionEvent[] {
const session = Session.create(SessionId('records'))
for (const value of data) appendPluginRecord(session, 'plugin:action-cards/decision', value)
return session.snapshotEvents()
}
const fold = (events: readonly SessionEvent[], state = init()) => events.reduce(projection.apply, state)
it('admits a successful propose_action call as a card in its turn and ignores failed, malformed and other calls', () => {
const state = fold([turn(1), call('c1'), result('c1')])
expect(state.view.proposals).toEqual({ c1: { callId: 'c1', turn: 1, ...args } })
expect(state.calls).toEqual({})
for (const events of [
[call('c2'), result('c2', true)],
[call('c3', '{broken')],
[call('c4', { ...args, title: ' ' })],
[call('c5', { ...args, details: 'text' })],
[call('c6', args, 1, 'other_tool'), result('c6')],
[result('unrelated')],
]) {
expect(fold(events).view.proposals).toEqual({})
}
const trimmed = fold([call('c7', { ...args, title: ' Counter ' }), result('c7')])
expect(trimmed.view.proposals.c7?.title).toBe('Counter')
const pending = fold([call('c8')])
expect(Object.keys(pending.calls)).toEqual(['c8'])
expect(projection.apply(pending, turn(2)).calls).toEqual({})
const idle = fold([turn(3)])
expect(projection.apply(idle, turn(4))).toBe(idle)
})
it('keeps the first admitted card of a turn and replaces only unstarted earlier cards', () => {
const first = fold([turn(1), call('a'), result('a')])
const late = fold([call('b'), result('b')], first)
expect(Object.keys(late.view.proposals)).toEqual(['a'])
const next = fold([turn(2), call('c', args, 2), result('c', false, 2)], first)
expect(next.view.decisions).toEqual({ a: { callId: 'a', action: 'test_action', phase: 'replaced' } })
const started = fold(records({ callId: ToolCallId('a'), action: 'test_action', phase: 'creating' }), first)
const afterStart = fold([turn(2), call('c', args, 2), result('c', false, 2)], started)
expect(afterStart.view.decisions.a?.phase).toBe('creating')
expect(afterStart.view.decisions.c).toBeUndefined()
// Replacement crosses kinds: a card of another action replaces the unstarted one too.
const other = fold([turn(2), call('d', { ...args, action: 'other_action' }, 2), result('d', false, 2)], first)
expect(other.view.decisions).toEqual({ a: { callId: 'a', action: 'test_action', phase: 'replaced' } })
})
it('counts user messages queued for later turns for the card tool and leaves cards to it', () => {
const queued = fold([turn(1), spliced(1, undefined, 'next-step'), spliced(2), call('a'), result('a')])
expect(queued.queued).toBe(2)
expect(Object.keys(queued.view.proposals)).toEqual(['a'])
expect(queued.view.decisions).toEqual({})
expect(fold([spliced(0, 2)], queued).queued).toBe(0)
expect(fold([spliced(0, 5)]).queued).toBe(0)
})
it('ignores the events a forked Session inherits from its parent', () => {
const parent = [turn(1), call('a'), result('a')]
// Every event created so far precedes the fork point.
const inherited = init(seq)
expect(fold(parent, inherited)).toBe(inherited)
const own = fold([turn(2), call('b', args, 2), result('b', false, 2)], inherited)
expect(Object.keys(own.view.proposals)).toEqual(['b'])
expect(own.view.decisions).toEqual({})
})
it('records valid decisions as pending status until a status notice acknowledges them', () => {
const [created, malformed, dismissed] = records(
{ callId: ToolCallId('a'), action: 'test_action', phase: 'creating', data: { value: 1 } },
{ callId: ToolCallId('a'), action: 'test_action', phase: '' },
{ callId: ToolCallId('b'), action: 'test_action', phase: 'dismissed' },
)
const state = fold([created!, malformed!, dismissed!])
expect(state.view.decisions).toEqual({
a: { callId: 'a', action: 'test_action', phase: 'creating', data: { value: 1 } },
b: { callId: 'b', action: 'test_action', phase: 'dismissed' },
})
expect(state.pendingStatus).toEqual({ a: created!.seq, b: dismissed!.seq })
const notice = (through: number): SessionEvent<'user/message'> => ({ type: 'user/message', seq: SessionSeq(99), time: 0, surfaceOp: 'append',
data: createUserMessage({ content: [{ type: 'text', text: 'status' }],
source: { kind: 'action-cards', through: SessionSeq(through), form: 'notice', summary: 'status' } }) })
expect(projection.apply(state, notice(created!.seq)).pendingStatus).toEqual({ b: dismissed!.seq })
const user: SessionEvent<'user/message'> = { type: 'user/message', seq: SessionSeq(100), time: 0, surfaceOp: 'append',
data: createUserMessage({ content: [{ type: 'text', text: 'hi' }], source: { kind: 'user' } }) }
expect(projection.apply(state, user)).toBe(state)
})
it('publishes proposals and decisions as one view reused across internal state changes', () => {
const state = fold([turn(1), call('a'), result('a')])
const view = projection.wire.view(state)
expect(view).toBe(state.view)
expect(projection.wire.viewSchema.parse(view)).toEqual(view)
expect(projection.wire.view(fold([spliced(1), call('pending')], state))).toBe(view)
expect(projection.stateSchema.parse(state)).toEqual(state)
})
@@ -0,0 +1,71 @@
import { expect, it, vi } from 'vitest'
import { agentEvents, type Agent, type PreStepDecision } from '@deepseek-ai/dsh-agent'
import { ToolCallId } from '@deepseek-ai/dsh-llm'
import { appendPluginRecord } from '@deepseek-ai/dsh-session'
import ActionCards from '../src/index.ts'
import { ACTION, callId, fixture } from './fixture.ts'
type Fixture = Awaited<ReturnType<typeof fixture>>
async function prepare(f: Fixture, agent: Agent = f.agent, signal = new AbortController().signal, admitted?: () => void | Promise<void>) {
const decision = await agentEvents(f.ctx, agent).waterfall('agent/pre-step', { messages: [], turn: 2, step: 1, signal, modelSelection: { provider: 'mock', model: 'mock' } },
async (): Promise<PreStepDecision> => { await admitted?.(); return { kind: 'enter', messages: [] } })
return decision.kind === 'reject' ? decision : { ...decision, messages: decision.messages.filter(message => message.source.kind === 'action-cards') }
}
function enter(agent: Agent, decision: PreStepDecision): void {
if (decision.kind === 'enter') for (const message of decision.messages) agent.session.append('user/message', message, { surfaceOp: 'append' })
}
it('reports recorded decisions once on the next request, with each kind guidance once, without waking the conversation', async () => {
const f = await fixture({ statusGuidance: 'Failed runs can be retried from their card.', statusFields: ['reason'] })
const followup = vi.spyOn(f.agent, 'followup')
expect(await prepare(f)).toEqual({ kind: 'enter', messages: [] })
f.propose()
f.propose(ToolCallId('call-card-2'), { action: ACTION, title: 'Second', brief: 'Another card.', details: { value: 'b' } }, f.agent, 2)
expect(await prepare(f)).toEqual({ kind: 'enter', messages: [] })
f.ctx.actionCards.record(f.agent, { callId, action: ACTION, phase: 'failed', data: { reason: 'timeout', internal: 'x' } })
f.ctx.actionCards.record(f.agent, { callId: ToolCallId('call-card-2'), action: ACTION, phase: 'dismissed' })
// A decision logged by a kind that is no longer registered is still reported.
appendPluginRecord(f.agent.session, 'plugin:action-cards/decision', { callId: ToolCallId('call-orphan'), action: 'retired_action', phase: 'done' })
const decision = await prepare(f)
if (decision.kind !== 'enter') throw new Error('request was rejected')
expect(decision.messages).toHaveLength(1)
expect(decision.messages[0]!.source).toMatchObject({ kind: 'action-cards', form: 'notice', summary: 'Action card status updated' })
expect(decision.messages[0]!.content).toMatchSnapshot('status notice')
enter(f.agent, decision)
expect(await prepare(f)).toEqual({ kind: 'enter', messages: [] })
expect(followup).not.toHaveBeenCalled()
})
it('keeps undelivered status through rejection, cancellation and plugin reload', async () => {
const f = await fixture()
f.propose()
await f.ctx.actionCards.act(f.agent, callId, 'dismiss')
const cancelled = new AbortController()
await expect(prepare(f, f.agent, cancelled.signal, () => { cancelled.abort() })).rejects.toThrow()
const rejected = f.ctx.on('agent/pre-step', async (): Promise<PreStepDecision> => ({ kind: 'reject' }))
expect((await prepare(f)).kind).toBe('reject')
rejected()
expect(await prepare(f, f.agent, undefined, async () => { await f.cards.dispose() })).toEqual({ kind: 'enter', messages: [] })
await f.ctx.plugin(ActionCards, { queuedMessageLimit: 3 })
const decision = await prepare(f)
if (decision.kind !== 'enter') throw new Error('request was rejected')
expect(JSON.stringify(decision.messages[0]!.content)).toContain('dismissed')
})
it('adds nothing when cancellation arrives after downstream preparation', async () => {
const f = await fixture()
f.propose()
await f.ctx.actionCards.act(f.agent, callId, 'dismiss')
await f.cards.dispose()
const controller = new AbortController()
// Prepended before the card service returns, so it runs right after the service's own listener.
f.ctx.on('agent/pre-step', async (_event, next) => {
const decision = await next()
controller.abort()
return decision
}, { prepend: true })
await f.ctx.plugin(ActionCards)
expect(await prepare(f, f.agent, controller.signal)).toEqual({ kind: 'enter', messages: [] })
})
@@ -0,0 +1,198 @@
import { expect, it, vi } from 'vitest'
import { createUserMessage, ToolCallId } from '@deepseek-ai/dsh-llm'
import { SessionId } from '@deepseek-ai/dsh-session'
import type { ToolExecutionToken } from '@deepseek-ai/dsh-tools'
import { executeModelToolFixture, executeToolFixture } from '@deepseek-ai/dsh-tool-testkit'
import type { Agent } from '@deepseek-ai/dsh-agent'
import { MockAdapter, textResponse, toolCallResponse } from '../../../core/agent-loop/tests/mock-adapter.ts'
import { z } from 'zod'
import type { ActionCardKind, ActionCardResult } from '../src/index.ts'
import { ACTION, fixture, INVITATION, proposal, SKILL, type TestDetails } from './fixture.ts'
type Fixture = Awaited<ReturnType<typeof fixture>>
const signal = new AbortController().signal
const modelSelection = { provider: 'mock', model: 'mock' }
/** Run one call through the executor with the step admission the loop would resolve. */
async function call(f: Fixture, name: string, args: object, id: string, agent: Agent = f.agent, parent?: ToolExecutionToken) {
const toolSet = await f.ctx.tools.resolve({ agent, modelSelection, signal })
try {
return parent === undefined
? await executeModelToolFixture(f.ctx, { callId: ToolCallId(id), name, arguments: args, agent, signal, toolSet })
: await executeToolFixture(f.ctx, { callId: ToolCallId(id), name, arguments: args, agent, signal, toolSet, parent })
} finally { toolSet.dispose() }
}
async function declared(f: Fixture, agent: Agent = f.agent): Promise<boolean> {
const toolSet = await f.ctx.tools.resolve({ agent, modelSelection, signal })
const visible = toolSet.view(agent).has('propose_action')
toolSet.dispose()
return visible
}
function text(result: { content: readonly { type: string; text?: string }[] }): string {
return result.content.map(block => block.text ?? '').join('')
}
it('declares propose_action from the step after a card skill loads, and logs the addition', async () => {
const f = await fixture()
const adapter = new MockAdapter([
toolCallResponse('call-skill', 'skill', { name: SKILL }),
toolCallResponse('call-card-1', 'propose_action', proposal),
textResponse('The card is below.'),
])
f.ctx.llm.registerAdapter(['mock'], adapter)
const agent = await f.harness.create(SessionId('loop'), modelSelection)
expect(await declared(f, agent)).toBe(false)
agent.followup(createUserMessage({ content: [{ type: 'text', text: 'I want a counter.' }], source: { kind: 'user' } }))
await agent.whenIdle()
expect(adapter.requests.map(request => (request.tools ?? []).some(tool => tool.name === 'propose_action'))).toEqual([false, true, true])
const tool = adapter.requests[1]!.tools!.find(candidate => candidate.name === 'propose_action')
expect(tool).toMatchSnapshot('tool declaration')
expect(agent.session.snapshotEvents().flatMap(event => event.type === 'developer/message' ? event.data.message.content : []))
.toEqual([{ type: 'tool-addition', toolName: 'propose_action' }])
expect(await declared(f, agent)).toBe(true)
const result = agent.session.snapshotEvents().find(event => event.type === 'tool/result' && event.data.message.toolCallId === 'call-card-1')
expect(result?.type === 'tool/result' && result.data.message.content).toEqual([
{ type: 'text', text: `Card shown below your answer. Do not restate it. ${INVITATION}` },
])
expect(f.view(agent).proposals['call-card-1']).toEqual({ callId: 'call-card-1', turn: 1, ...proposal })
})
it('declares nothing for other skills and stops declaring when the kind unregisters', async () => {
const f = await fixture()
f.agent.session.append('turn/start', { turn: 1 })
expect((await call(f, 'skill', { name: 'other-skill' }, 'other')).isError).toBe(false)
expect((await call(f, 'skill', { name: 'missing-skill' }, 'missing')).isError).toBe(true)
expect(await declared(f)).toBe(false)
expect((await call(f, 'propose_action', proposal, 'early')).isError).toBe(true)
expect((await call(f, 'skill', { name: SKILL }, 'load')).isError).toBe(false)
expect((await call(f, 'skill', { name: SKILL }, 'reload')).isError).toBe(false)
expect((await executeToolFixture(f.ctx, { callId: ToolCallId('detached'), name: 'skill', arguments: { name: SKILL }, signal })).isError).toBe(false)
expect(await declared(f)).toBe(true)
await f.kindFiber.dispose()
expect(await declared(f)).toBe(false)
await f.cards.dispose()
expect(f.ctx.tools.get('propose_action')).toBeUndefined()
})
it('refuses unknown actions, a skill not loaded in this turn, blank fields, invalid details and nested calls', async () => {
const f = await fixture()
f.agent.session.append('turn/start', { turn: 1 })
await call(f, 'skill', { name: SKILL }, 'load')
expect(text(await call(f, 'propose_action', { ...proposal, action: 'nope' }, 'unknown'))).toBe('Error: Unknown action "nope". Registered actions: test_action.')
expect(text(await call(f, 'propose_action', { ...proposal, title: ' ' }, 'blank'))).toBe('Error: Write a title and a brief.')
expect(text(await call(f, 'propose_action', { ...proposal, brief: '' }, 'blank-brief'))).toBe('Error: Write a title and a brief.')
expect(text(await call(f, 'propose_action', { ...proposal, details: { value: 1 } }, 'invalid')))
.toBe('Error: Invalid details: details.value: Invalid input: expected string, received number')
expect(text(await call(f, 'propose_action', proposal, 'nested', f.agent, Symbol('outer') as ToolExecutionToken)))
.toBe('Error: Call propose_action directly, not from code.')
f.agent.session.append('turn/start', { turn: 2 })
expect(text(await call(f, 'propose_action', proposal, 'stale'))).toBe('Error: Load skill test-skill first, then call propose_action.')
await f.kindFiber.dispose()
await call(f, 'skill', { name: SKILL }, 'load-again')
expect(await declared(f)).toBe(false)
expect(Object.keys(f.view().proposals)).toEqual([])
})
it('accepts one card per turn, admits a retry after a failed result and names the registered actions', async () => {
const f = await fixture()
f.agent.session.append('turn/start', { turn: 1 })
await call(f, 'skill', { name: SKILL }, 'load')
const blocked = f.ctx.on('tools/post-execute', async (exec, _result, next) => exec.callId === 'blocked'
? { kind: 'block', feedback: [{ type: 'text', text: 'blocked' }] } : await next())
expect((await call(f, 'propose_action', proposal, 'blocked')).isError).toBe(true)
blocked()
const accepted = await call(f, 'propose_action', { ...proposal, title: ' Counter ' }, 'accepted')
expect(accepted).toMatchObject({ isError: false, value: { action: ACTION, title: 'Counter', invitation: INVITATION } })
expect(text(accepted)).toBe(`Card shown below your answer. Do not restate it. ${INVITATION}`)
const second = text(await call(f, 'propose_action', proposal, 'second'))
// Only an accepted call tells the model how to invite the user to its card.
expect(second).toBe('Error: One card per turn; the earlier card stands.')
expect(second).not.toContain(INVITATION)
f.agent.session.append('turn/start', { turn: 2 })
await call(f, 'skill', { name: SKILL }, 'load-2')
f.propose(ToolCallId('logged'), proposal, f.agent, 2)
expect(text(await call(f, 'propose_action', proposal, 'after-log'))).toBe('Error: One card per turn; the earlier card stands.')
// Disposing the agent releases its activation.
expect(await declared(f)).toBe(true)
await f.agentHandle.dispose()
expect(await declared(f)).toBe(false)
})
it('activates nothing for a skill load dispatched from code, so a PTC preset never declares the tool', async () => {
const f = await fixture()
f.agent.session.append('turn/start', { turn: 1 })
const outer = Symbol('outer') as ToolExecutionToken
expect((await call(f, 'skill', { name: SKILL }, 'nested-load', f.agent, outer)).isError).toBe(false)
expect(await declared(f)).toBe(false)
expect((await call(f, 'propose_action', proposal, 'unadmitted')).isError).toBe(true)
expect((await call(f, 'skill', { name: SKILL }, 'direct-load')).isError).toBe(false)
expect(await declared(f)).toBe(true)
})
it('offers cards in the main conversation only: subagent skill loads activate nothing and non-root calls are refused', async () => {
const f = await fixture()
const child = (await f.ctx.agents.create({ sessionId: SessionId('cards-child'), parentAgent: f.agent,
meta: { parentSession: f.agent.id, origin: 'subagent' } })).agent
child.session.append('turn/start', { turn: 1 })
expect((await call(f, 'skill', { name: SKILL }, 'child-load', child)).isError).toBe(false)
expect(await declared(f, child)).toBe(false)
expect((await call(f, 'propose_action', proposal, 'child-card', child)).isError).toBe(true)
f.agent.session.append('turn/start', { turn: 1 })
await call(f, 'skill', { name: SKILL }, 'load')
// Admission is fixed when the step starts; the call still checks that its agent is a live root.
const toolSet = await f.ctx.tools.resolve({ agent: f.agent, modelSelection, signal })
vi.spyOn(f.ctx.agents, 'roots').mockReturnValue([])
const refused = await executeModelToolFixture(f.ctx, { callId: ToolCallId('detached-root'), name: 'propose_action', arguments: proposal,
agent: f.agent, signal, toolSet })
toolSet.dispose()
expect(text(refused)).toBe('Error: Cards are offered in the main conversation only.')
})
it('names no actions when the last kind unregisters after the step admitted the tool', async () => {
const f = await fixture()
f.agent.session.append('turn/start', { turn: 1 })
await call(f, 'skill', { name: SKILL }, 'load')
const toolSet = await f.ctx.tools.resolve({ agent: f.agent, modelSelection, signal })
await f.kindFiber.dispose()
const result = await executeModelToolFixture(f.ctx, { callId: ToolCallId('late'), name: 'propose_action', arguments: proposal,
agent: f.agent, signal, toolSet })
toolSet.dispose()
expect(text(result)).toBe('Error: Unknown action "test_action". Registered actions: none.')
})
it('refuses a card while the user has queued messages for later turns, so the log holds no card', async () => {
const f = await fixture()
const queued = () => createUserMessage({ content: [{ type: 'text', text: 'Next question.' }], source: { kind: 'user' } })
const live: { agent?: Agent } = {}
const adapter = new MockAdapter([
toolCallResponse('call-skill', 'skill', { name: SKILL }),
() => {
// The user queues two more messages while the model writes its answer.
live.agent!.inbox.append('next-turn', queued())
live.agent!.inbox.append('next-turn', queued())
return toolCallResponse('call-card-1', 'propose_action', proposal)
},
textResponse('Short answer.'),
textResponse('Next answer.'),
textResponse('Last answer.'),
])
f.ctx.llm.registerAdapter(['mock'], adapter)
const agent = await f.harness.create(SessionId('queued'), modelSelection)
live.agent = agent
agent.followup(createUserMessage({ content: [{ type: 'text', text: 'I want a counter.' }], source: { kind: 'user' } }))
await agent.whenIdle()
const result = agent.session.snapshotEvents().find(event => event.type === 'tool/result' && event.data.message.toolCallId === 'call-card-1')
expect(result?.type === 'tool/result' && result.data.message).toMatchObject({ isError: true,
content: [{ type: 'text', text: 'Error: The user has queued more messages; show no card and answer briefly.' }] })
expect(f.view(agent)).toEqual({ proposals: {}, decisions: {} })
})
it('requires every kind to give the invitation its accepted calls return', () => {
const kind = { action: 'other_action', skill: 'other-skill', details: z.object({ value: z.string() }), verbs: {},
act: () => Promise.resolve<ActionCardResult>({ status: 'accepted' }) }
// @ts-expect-error: a kind without its invitation does not compile.
const incomplete: ActionCardKind<TestDetails> = kind
expect(incomplete).toBe(kind)
})
@@ -0,0 +1,24 @@
{
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"rootDir": "src",
"outDir": "lib/types"
},
"references": [
{ "path": "../../../vendor/cordis" },
{ "path": "../../typert/protocol" },
{ "path": "../../llm/llm" },
{ "path": "../../core/tools" },
{ "path": "../../core/agent" },
{ "path": "../../core/session" },
{ "path": "../../session/session-projection" },
{ "path": "../../api/remote-access/tsconfig.host.json" },
{ "path": "../../sandbox/sandbox-policy" },
{ "path": "../../util/values" }
],
"files": [
"src/index.ts",
"src/types.ts",
"src/projection.ts"
]
}
@@ -0,0 +1,15 @@
import { defineConfig } from 'tsdown'
/** Build the action card plugin. */
export default defineConfig([
{
entry: ['lib/types/index.js'],
outDir: 'lib',
format: ['esm'],
platform: 'node',
target: 'es2024',
fixedExtension: false,
dts: false,
clean: false,
},
])
@@ -0,0 +1,40 @@
# Bilingual-pair consistency record for README.md (docs/i18n/README.md): per heading
# section, a hash of its English and Chinese blocks outside code blocks and generated regions.
# After editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write packages/experimental/client-ui-action-cards/README.md
/:
en: eec43de9002f4ab1
zh: 0d2fddca5a1f993a
/deepseek-ai-dsh-client-ui-action-cards:
en: c36a554629f96f79
zh: 0ea837da87938e52
/deepseek-ai-dsh-client-ui-action-cards/summary:
en: 89ea57734c42a481
zh: c7a4f5f0b59b78ad
/deepseek-ai-dsh-client-ui-action-cards/table-of-contents:
en: d152484eb41ac6b4
zh: b00dddf4c88f0a45
/deepseek-ai-dsh-client-ui-action-cards/use-this-package:
en: 163d385e91a5df38
zh: 95bdde9e844cca51
/deepseek-ai-dsh-client-ui-action-cards/use-this-package/register-a-card-body:
en: 9ec9f9f8f3165d3e
zh: 2d8154ec2fe0c9ab
/deepseek-ai-dsh-client-ui-action-cards/understand-the-implementation:
en: 168ae39a83d6f4eb
zh: 312cc12876bca834
/deepseek-ai-dsh-client-ui-action-cards/further-exploration:
en: 55e4d74a33cdeeb3
zh: f601d5a4dcf63ec5
/deepseek-ai-dsh-client-ui-action-cards/model-experience:
en: f80a4b656c108f22
zh: a3e30f46fa7136ac
/deepseek-ai-dsh-client-ui-action-cards/model-experience/kv-cache-effect:
en: 9cb7131bde146341
zh: 1c7d1af00f58004d
/deepseek-ai-dsh-client-ui-action-cards/known-limitations-and-deferred-work:
en: df94821940e4e2b8
zh: 490441ab06b153c9
/deepseek-ai-dsh-client-ui-action-cards/known-limitations-and-deferred-work/dev-note:
en: e67f633dc6fe7773
zh: bd8a8936229e035e
@@ -0,0 +1,89 @@
---
description: "Browser half of optional action cards: the card frame below completed answers, the body registry that experiment packages fill, and the one-line process row of propose_action calls."
kind: "package-reference"
---
# @deepseek-ai/dsh-client-ui-action-cards
English | [中文](README.zh.md)
## Summary
Users see an optional action card below a completed answer: its title, a brief that folds when long, and the controls of the experiment that proposed it. In the process area, the proposal shows as one row with the card title and whether the call was accepted; a refused call expands to its reason. A card whose experiment provides no controls in this browser offers only Not now. Showing, expanding and folding cards make no model request.
## Table of Contents
- [Use this package](#use-this-package)
- [Understand the implementation](#understand-the-implementation)
- [Further Exploration](#further-exploration)
- [Model Experience](#model-experience)
- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
- [Dev Note](#dev-note)
-----
<a id="use-this-package"></a>
## Use this package
Enable the [Personalized pages](../creation-recommendations/README.md) or [Plugin recommendations](../plugin-recommendations/README.md) bundle; each inserts this package beside its [Host service](../action-cards/README.md). This browser contribution has no configuration fields.
### Register a card body
A kind's browser plugin injects `actionCards` and registers its body inside its own `ctx.effect`: `ctx.actionCards.register({ action, locale, Body })` returns the disposer. The frame renders `Body` for cards of that action with the card, its recorded or derived decision, the Session id, the frame's title and folded brief as `summary`, the frame's fold component as `Folded`, an `act(verb, payload?)` bound to the card, and a translator `t` for the body's own locale namespace. The body places `summary` in its own layout and owns its header, controls, status and footer. A second body for the same action is a registration error.
-----
<a id="understand-the-implementation"></a>
## Understand the implementation
<details>
<summary>Implementation internals — click to expand</summary>
One `conversation.chat.turnTail` entry reads the `actionCards` Session projection and shows the card admitted in its Turn after that Turn has ended with a final answer; a Turn that ends without answer text shows no card, and should several cards share a Turn, the lowest call id is shown. The frame sets `data-action-card` to the call id and names its region by the card title. Folding counts a wide grapheme, such as a Chinese character, as two Latin-letter widths and folds after about 260; it never measures layout or alters the full text.
A `tool.call.toolview` entry keyed `propose_action` renders the process row from the recorded call arguments and result. The plugin provides the body registry at its top level, then mounts the generated `actionCards` Remote and registers its dictionaries and slot entries for the same lifetime.
| Source | Contents |
|---|---|
| [`src/client/mount.ts`](src/client/mount.ts) | Registry, Remote, dictionaries and slot entries |
| [`src/client/Card.tsx`](src/client/Card.tsx) | Turn tail, frame, fold and fallback body |
| [`src/client/ProposeRow.tsx`](src/client/ProposeRow.tsx) | Process row |
| [`src/client/registry.ts`](src/client/registry.ts) | Body registry and body props |
| [`tests/`](tests/) | Placement, frame, fallback, row and registration tests |
</details>
-----
<a id="further-exploration"></a>
## Further Exploration
- [Action cards](../action-cards/README.md) — the tool, card state and user actions behind the frame.
- [Personalized page cards](../client-ui-creation-recommendations/README.md) and [recommendation cards](../client-ui-plugin-recommendations/README.md) — the shipped card bodies.
- [Slots](../../client/ui-slots/README.md) — Turn tail and tool row registration.
<a id="model-experience"></a>
## Model Experience
Indirectly, through the Host card service: card actions reach it as Remote calls, and it owns the logged tool, decisions and status notice.
#### KV Cache effect
Rendering cards and process rows does not change any model request.
## Known Limitations and Deferred Work
<a id="known-limitations-and-deferred-work"></a>
- A card whose action has no registered body offers only Not now, so its experiment's other actions need that experiment's browser plugin. Not now still works after the experiment's Host plugin is turned off.
- The Turn tail shows one card per Turn, matching the Host rule of one card per turn.
<a id="dev-note"></a>
### Dev Note
<details>
<summary>Working context for maintainers — click to expand</summary>
None.
</details>
@@ -0,0 +1,89 @@
---
description: "可选操作卡片的浏览器端:已完成回答下方的卡片框架、供各实验包填入的正文注册表,以及 propose_action 调用的单行过程行。"
kind: "package-reference"
---
# @deepseek-ai/dsh-client-ui-action-cards
[English](README.md) | 中文
## 概述
用户在已完成的回答下方看到一张可选操作卡片:标题、过长时会折叠的说明,以及提议该卡片的实验所提供的控件。在过程区中,提议显示为一行,带卡片标题和调用是否被接受;被拒绝的调用可以展开查看原因。如果实验在当前浏览器中没有提供控件,卡片只提供“暂不需要”。展示、展开和折叠卡片都不会发起模型请求。
## 目录
- [使用本包](#use-this-package)
- [实现原理](#understand-the-implementation)
- [延伸阅读](#further-exploration)
- [模型体验](#model-experience)
- [已知限制与后续工作](#known-limitations-and-deferred-work)
- [开发备注](#dev-note)
-----
<a id="use-this-package"></a>
## 使用本包
启用[个性化页面](../creation-recommendations/README.zh.md)或[插件推荐](../plugin-recommendations/README.zh.md) Bundle,它们都会在本包旁插入对应的 [Host 服务](../action-cards/README.zh.md)。本浏览器组件没有配置字段。
### 注册卡片正文
种类的浏览器插件注入 `actionCards`,并在自己的 `ctx.effect` 中注册正文:`ctx.actionCards.register({ action, locale, Body })` 返回注销函数。框架为该动作的卡片渲染 `Body`,并传入:卡片、已记录或推导出的决定、会话 id、框架渲染的标题与折叠说明 `summary`、框架的折叠组件 `Folded`、绑定到该卡片的 `act(verb, payload?)`,以及正文自己语言命名空间的翻译函数 `t`。正文把 `summary` 放进自己的布局,并负责自己的标题栏、控件、状态和底部操作。为同一动作注册第二个正文属于注册错误。
-----
<a id="understand-the-implementation"></a>
## 实现原理
<details>
<summary>实现细节——点击展开</summary>
一个 `conversation.chat.turnTail` 条目读取 `actionCards` 会话投影,在某一轮以最终回答结束后展示该轮接受的卡片;没有回答文本就结束的轮次不显示卡片,若同一轮有多张卡片,显示调用 id 最小的一张。框架把 `data-action-card` 设为调用 id,并以卡片标题命名所在区域。折叠时,宽字素(如汉字)按两个拉丁字母宽度计算,约 260 个单位后折叠;折叠不测量布局,也不改动完整文本。
一个以 `propose_action` 为键的 `tool.call.toolview` 条目,根据记录的调用参数和结果渲染过程行。插件在顶层提供正文注册表,然后挂载生成的 `actionCards` Remote,并在同一生命周期内注册词典和插槽条目。
| 源码 | 内容 |
|---|---|
| [`src/client/mount.ts`](src/client/mount.ts) | 注册表、Remote、词典与插槽条目 |
| [`src/client/Card.tsx`](src/client/Card.tsx) | 轮次尾部、框架、折叠与兜底正文 |
| [`src/client/ProposeRow.tsx`](src/client/ProposeRow.tsx) | 过程行 |
| [`src/client/registry.ts`](src/client/registry.ts) | 正文注册表与正文属性 |
| [`tests/`](tests/) | 位置、框架、兜底、过程行与注册测试 |
</details>
-----
<a id="further-exploration"></a>
## 延伸阅读
- [操作卡片](../action-cards/README.zh.md) — 框架背后的工具、卡片状态与用户操作。
- [个性化页面卡片](../client-ui-creation-recommendations/README.zh.md)和[推荐卡片](../client-ui-plugin-recommendations/README.zh.md) — 随产品提供的卡片正文。
- [插槽](../../client/ui-slots/README.zh.md) — 轮次尾部与工具行的注册。
<a id="model-experience"></a>
## 模型体验
间接影响来自 Host 卡片服务:卡片操作以 Remote 调用到达该服务,由它负责记录在日志中的工具、决定和状态通知。
#### KV Cache 影响
渲染卡片和过程行不会改变任何模型请求。
## 已知限制与后续工作
<a id="known-limitations-and-deferred-work"></a>
- 动作没有注册正文的卡片只提供“暂不需要”,该实验的其他操作需要它的浏览器插件。即使该实验的 Host 插件已关闭,“暂不需要”仍然可用。
- 轮次尾部每轮只显示一张卡片,与 Host 每轮一张卡片的规则一致。
<a id="dev-note"></a>
### 开发备注
<details>
<summary>维护者工作背景——点击展开</summary>
无。
</details>
@@ -0,0 +1,6 @@
{
"meta": {
"title": "Anzeige von Aktionskarten",
"description": "Zeigt optionale Aktionskarten unter Antworten mit den eigenen Bedienelementen jedes Experiments an."
}
}
@@ -0,0 +1,6 @@
{
"meta": {
"title": "Action card display",
"description": "Show optional action cards below answers, with each experiment's own controls."
}
}
@@ -0,0 +1,6 @@
{
"meta": {
"title": "Visualización de tarjetas de acción",
"description": "Muestra tarjetas de acción opcionales debajo de las respuestas, con los controles propios de cada experimento."
}
}
@@ -0,0 +1,6 @@
{
"meta": {
"title": "Affichage des cartes d'action",
"description": "Affiche des cartes d'action facultatives sous les réponses, avec les commandes propres à chaque expérience."
}
}
@@ -0,0 +1,6 @@
{
"meta": {
"title": "アクションカード表示",
"description": "回答の下に任意のアクションカードを表示します。カードの内容は各実験が提供します。"
}
}
@@ -0,0 +1,6 @@
{
"meta": {
"title": "Exibição de cartões de ação",
"description": "Mostra cartões de ação opcionais abaixo das respostas, com os controles próprios de cada experimento."
}
}
@@ -0,0 +1,6 @@
{
"meta": {
"title": "Отображение карточек действий",
"description": "Показывает необязательные карточки действий под ответами с элементами управления каждого эксперимента."
}
}
@@ -0,0 +1,6 @@
{
"meta": {
"title": "操作卡片显示",
"description": "在回答下方显示可选操作卡片,卡片内容由各实验提供。"
}
}
@@ -0,0 +1,71 @@
{
"version": "0.2.1-rc.2",
"publishConfig": {
"access": "public"
},
"type": "module",
"main": "lib/index.js",
"types": "lib/types/index.d.ts",
"license": "MIT",
"name": "@deepseek-ai/dsh-client-ui-action-cards",
"description": "Action cards below completed answers: the shared card frame, kind bodies and the compact tool row",
"repository": {
"type": "git",
"url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
"directory": "packages/experimental/client-ui-action-cards"
},
"exports": {
".": {
"types": "./lib/types/index.d.ts",
"default": "./lib/index.js"
},
"./src/*": "./src/*",
"./package.json": "./package.json",
"./client": {
"types": "./lib/types/client/index.d.ts",
"default": "./lib/client.js"
},
"./locale/*.json": "./locale/*.json"
},
"dsh": {
"client": {
"platform": "web",
"inject": [
"@deepseek-ai/dsh-api-remotes",
"@deepseek-ai/dsh-client-locale",
"@deepseek-ai/dsh-client-ui-conversation",
"@deepseek-ai/dsh-client-ui-chat",
"@deepseek-ai/dsh-client-ui-session",
"@deepseek-ai/dsh-client-ui-tool"
]
}
},
"peerDependencies": {
"@deepseek-ai/cordis": "workspace:~"
},
"devDependencies": {
"@deepseek-ai/cordis": "workspace:~",
"@deepseek-ai/dsh-llm": "workspace:*",
"@deepseek-ai/dsh-session": "workspace:*",
"@deepseek-ai/dsh-client-ui-renderer": "workspace:*",
"@deepseek-ai/dsh-api-remotes": "workspace:*",
"@deepseek-ai/dsh-client-locale": "workspace:*",
"@deepseek-ai/dsh-client-ui-conversation": "workspace:*",
"@deepseek-ai/dsh-client-ui-chat": "workspace:*",
"@deepseek-ai/dsh-client-ui-session": "workspace:*",
"@deepseek-ai/dsh-client-ui-slots": "workspace:*",
"@deepseek-ai/dsh-client-ui-primitives": "workspace:*",
"@deepseek-ai/dsh-client-ui-tool": "workspace:*",
"react": "^18.2.0",
"@deepseek-ai/dsh-action-cards": "workspace:*",
"@deepseek-ai/dsh-typert-protocol": "workspace:*",
"@deepseek-ai/dsh-util-values": "workspace:*",
"@deepseek-ai/dsh-client-test-runtime": "workspace:*"
},
"files": [
"locale/*.json",
"lib/index.js",
"lib/client.js",
"lib/types/**/*.d.ts"
]
}
@@ -0,0 +1,7 @@
.card { margin: 16px 0; border: 0.5px solid var(--dsw-alias-border-l2); border-radius: var(--dsw-radius-lg); overflow: hidden; background: var(--dsw-alias-bg-layer-1); font-family: var(--dsw-font-family); color: var(--dsw-alias-label-primary); }
.body { padding: 12px 20px 20px; }
.title { display: block; font-size: 16px; font-weight: 500; line-height: 1.5; overflow-wrap: anywhere; }
.text { margin: 8px 0 0; font-size: 13px; line-height: 1.7; white-space: pre-wrap; overflow-wrap: anywhere; color: var(--dsw-alias-label-secondary); }
.toggle { margin: 8px 0 0; padding-inline: 0; height: auto; min-height: 24px; font-size: 12px; }
.status { margin: 12px 0 0; font-size: 12px; line-height: 1.6; color: var(--dsw-alias-label-secondary); }
.footer { display: flex; align-items: center; justify-content: flex-end; gap: 12px; margin-top: 12px; }
@@ -0,0 +1,150 @@
/** The shared card frame below a completed answer: title, folded brief, and the kind's body or a dismiss-only fallback. */
import { useCallback, useId, useMemo, useState, type ReactNode } from 'react'
import { Button } from '@deepseek-ai/dsh-client-ui-primitives'
import type { HostObservable, InjectFace, PropsLocale, PropsRuntime, Translate } from '@deepseek-ai/dsh-client-ui-slots'
import type { SessionId } from '@deepseek-ai/dsh-session/types'
import type { ToolCallId } from '@deepseek-ai/dsh-llm/brand'
import type {
ActionCardDecision, ActionCardFields, ActionCardProposal, ActionCardResult, ActionCardView,
} from '@deepseek-ai/dsh-action-cards/types'
import type {} from '@deepseek-ai/dsh-client-ui-chat/client'
import type { ActionCardKey } from './locales.ts'
import type { FoldedTextProps, RegisteredBody } from './registry.ts'
import css from './Card.module.css'
/** Session-bound actions and registered bodies, without a service in the component. */
export interface TailInjected {
/**
* Send one user action for a card of the displayed Session.
* @param callId - the card's call id.
* @param verb - `dismiss` or one of the kind's verbs.
* @param payload - kind-defined fields.
* @returns the Host result, or undefined when the reply was lost.
*/
act: (callId: ToolCallId, verb: string, payload?: ActionCardFields) => Promise<ActionCardResult | undefined>
/**
* Bind a body's own locale namespace.
* @param locale - namespace the body declared.
* @returns its translator.
*/
bind: (locale: string) => Translate
hooks: { bodies: HostObservable<ReadonlyMap<string, RegisteredBody>> }
sessionId: SessionId
}
type TailProps = Pick<PropsRuntime<'conversation.chat.turnTail'>, 'turn' | 'useProjection'>
& PropsLocale<'actionCards'> & InjectFace<TailInjected>
/** Graphemes drawn about twice as wide as a Latin letter: CJK scripts, CJK punctuation and fullwidth forms. */
const WIDE_GRAPHEME = /^[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul} -〿＀-⦆¢-₩]/u
/** Preview budget in Latin-letter widths, about two card lines at desktop width. */
const FOLD_WIDTH = 260
/**
* Fold omitted text without measuring layout; a wide grapheme counts twice so Chinese and English fold near the
* same line. The full text is never altered.
* @param props - text and its localized toggle labels.
* @returns the paragraph and, when text was omitted, its toggle.
*/
export function FoldedText({ text, expandLabel, collapseLabel }: FoldedTextProps) {
const id = useId()
const [expanded, setExpanded] = useState(false)
const preview = useMemo(() => {
const compact = text.replace(/\s+/g, ' ').trim()
const characters = Array.from(new Intl.Segmenter(undefined, { granularity: 'grapheme' }).segment(compact), part => part.segment)
let width = 0
for (const [index, character] of characters.entries()) {
width += WIDE_GRAPHEME.test(character) ? 2 : 1
if (width > FOLD_WIDTH) return characters.slice(0, index).join('').trimEnd() + '…'
}
return undefined
}, [text])
return <>
<p id={id} className={css.text}>{expanded || preview === undefined ? text : preview}</p>
{preview !== undefined && <Button variant="ghost" size="sm" className={css.toggle} aria-expanded={expanded} aria-controls={id}
onClick={() => { setExpanded(value => !value) }}>{expanded ? collapseLabel : expandLabel}</Button>}
</>
}
/** The projection admits at most one card per turn; should several exist, the lowest call id is shown. */
function proposalOf(view: ActionCardView | undefined, turn: number): ActionCardProposal | undefined {
let found: ActionCardProposal | undefined
for (const proposal of Object.values(view?.proposals ?? {})) {
if (proposal.turn === turn && (found === undefined || proposal.callId < found.callId)) found = proposal
}
return found
}
function decisionOf(view: ActionCardView | undefined, callId: string): ActionCardDecision | undefined {
return view !== undefined && Object.hasOwn(view.decisions, callId) ? view.decisions[callId] : undefined
}
/**
* Place the card admitted in a turn after that turn's final answer, once the turn has ended with one.
* @param props - Turn location, projection reader, registered bodies and Session-bound actions.
* @returns the card, or null while the turn is open or when it ended without answer text.
*/
export function ActionCardTail({ turn, useProjection, ...props }: TailProps) {
const view = useProjection('actionCards')
const proposal = proposalOf(view, turn.turn)
return proposal === undefined || turn.end === undefined || turn.data.get('turn-tail')?.closing == null
? null : <ActionCard {...props} proposal={proposal} decision={decisionOf(view, proposal.callId)} />
}
/**
* Render the frame around one card.
* @param props - the card, its state, registered bodies and actions.
* @returns the card section.
*/
export function ActionCard({ proposal, decision, sessionId, act, bind, useBodies, t }: {
proposal: ActionCardProposal
decision: ActionCardDecision | undefined
} & InjectFace<TailInjected> & PropsLocale<'actionCards'>) {
const body = useBodies(bodies => bodies.get(proposal.action))
const send = useCallback((verb: string, payload?: ActionCardFields) => act(proposal.callId, verb, payload), [act, proposal.callId])
const summary = <>
<strong className={css.title}>{proposal.title}</strong>
<FoldedText text={proposal.brief} expandLabel={t('expandBrief')} collapseLabel={t('collapseBrief')} />
</>
// The frame re-renders on a locale change and hands the body a new summary, so the body re-renders with it.
const content = body === undefined
? <FallbackBody summary={summary} decision={decision} act={send} t={t} />
: body.render({ proposal, decision, sessionId, summary, Folded: FoldedText, act: send }, bind(body.locale))
return <section className={css.card} aria-label={proposal.title} data-action-card={proposal.callId} data-action={proposal.action}>
{content}
</section>
}
/** Phases the fallback can name; other kind phases leave only the summary. */
const NAMED_PHASES: Readonly<Record<string, ActionCardKey>> = { dismissed: 'dismissed', replaced: 'replaced' }
/** Title, brief and Not now for a card whose kind registered no body in this browser. */
function FallbackBody({ summary, decision, act, t }: {
summary: ReactNode
decision: ActionCardDecision | undefined
act: (verb: string) => Promise<ActionCardResult | undefined>
} & PropsLocale<'actionCards'>) {
const [pending, setPending] = useState(false)
const [error, setError] = useState<ActionCardKey>()
const phase = decision === undefined || !Object.hasOwn(NAMED_PHASES, decision.phase) ? undefined : NAMED_PHASES[decision.phase]
const dismiss = async () => {
setPending(true)
setError(undefined)
try {
const result = await act('dismiss')
if (result === undefined) setError('transport')
else if (result.status === 'refused' || result.status === 'unconfirmed') setError('unavailable')
} catch {
// A lost reply does not prove failure; replay supplies the durable decision.
setError('transport')
} finally { setPending(false) }
}
return <div className={css.body}>
{summary}
{phase !== undefined && <p role="status" className={css.status}>{t(phase)}</p>}
{error !== undefined && <p role="alert" className={css.status}>{t(error)}</p>}
{decision === undefined && <footer className={css.footer}>
<Button variant="ghost" size="sm" disabled={pending} onClick={() => { void dismiss() }}>{t('dismiss')}</Button>
</footer>}
</div>
}
@@ -0,0 +1,6 @@
/** The card title stays readable beside the call state. */
.summary { margin-left: 8px; display: flex; align-items: center; gap: 8px; min-width: 0; color: inherit; font-size: 12px; }
.summary > :first-child { flex-shrink: 0; }
.title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.reason { margin: 8px 0; padding: 12px; border-radius: var(--dsw-radius-lg); background: var(--dsw-alias-bg-layer-1); color: var(--dsw-alias-label-secondary); white-space: pre-wrap; overflow-wrap: anywhere; font-size: 12px; }
.inspect { align-self: flex-start; border: none; padding: 4px 0; background: transparent; color: var(--dsw-alias-link); font: inherit; font-size: 12px; cursor: pointer; }
@@ -0,0 +1,43 @@
/** One-line process row for a `propose_action` call: the card title and whether the card was shown. */
import { useState } from 'react'
import { DisclosureRow, IconAppsAddOutlineRegular } from '@deepseek-ai/dsh-client-ui-primitives'
import type { ToolCallViewProps } from '@deepseek-ai/dsh-client-ui-tool/client'
import type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots'
import css from './ProposeRow.module.css'
type ProposeRowProps = ToolCallViewProps & PropsLocale<'actionCards'>
/** Call states with row copy; a preparing call has no complete arguments yet. */
type RowState = 'preparing' | 'running' | 'ok' | 'error' | 'stopped'
function stateOf(props: ProposeRowProps): RowState {
if (props.phase === 'preparing') return 'preparing'
if (props.phase === 'start') return 'running'
if (props.block.error?.code === 'interrupted') return 'stopped'
return props.block.isError ? 'error' : 'ok'
}
/**
* Render a `propose_action` call from its recorded arguments and result. Only a refused call expands, to show
* why no card followed the answer.
* @param props - tool call and localized status copy.
* @returns the status row.
*/
export function ProposeRow(props: ProposeRowProps) {
const { block, inspect, t } = props
const [expanded, setExpanded] = useState(false)
const state = stateOf(props)
const title = block.args.text('title') ?? ''
const reason = props.phase === 'result' && props.block.isError
? props.block.content.flatMap(item => item.type === 'text' ? [item.text] : []).join('\n')
: ''
return <div data-tool="propose_action" data-state={state}>
<DisclosureRow title={t('row.title')} icon={<IconAppsAddOutlineRegular size={14} />} running={state === 'preparing' || state === 'running'}
open={expanded && reason !== ''} expandable={reason !== ''} expandOnRowClick keepContentWhenOpen
onToggle={() => { setExpanded(value => !value) }}
collapsedContent={<span className={css.summary}><span>{t(`row.${state}`)}</span><span className={css.title}>{title}</span></span>}>
<pre className={css.reason}>{reason}</pre>
{inspect && <button type="button" className={css.inspect} onClick={inspect}>{t('row.inspect')}</button>}
</DisclosureRow>
</div>
}
@@ -0,0 +1,15 @@
/** Bind generated Remote descriptors to the source-tested browser lifecycle. */
import type { Context } from '@deepseek-ai/cordis'
import remote from '@deepseek-ai/dsh-action-cards/remote'
import { mountActionCards } from './mount.ts'
export { inject } from './mount.ts'
export type { ActionCardBodyProps, ActionCardDescriptor, ActionCardRegistry, FoldedTextProps } from './registry.ts'
/**
* Mount action cards and their Remote.
* @param ctx - Client plugin context.
* @returns disposer for every contribution.
*/
export async function apply(ctx: Context): Promise<() => Promise<void>> {
return await mountActionCards(ctx, remote)
}
@@ -0,0 +1,82 @@
/** English card frame, fallback and tool-row copy. */
export const en = {
expandBrief: 'Show more', collapseBrief: 'Show less',
dismiss: 'Not now', dismissed: 'Dismissed', replaced: 'Replaced',
unavailable: 'This action is unavailable. Check the plugin in Plugins.',
transport: 'Could not confirm the result. Reconnect and check the card before retrying.',
'row.title': 'Action card', 'row.preparing': 'Preparing a card', 'row.running': 'Proposing',
'row.ok': 'Proposed', 'row.error': 'Not shown', 'row.stopped': 'Interrupted', 'row.inspect': 'Inspect call',
}
/** Keys shared by every dictionary. */
export type ActionCardKey = keyof typeof en
/** Chinese card frame, fallback and tool-row copy. */
export const zh: Record<ActionCardKey, string> = {
expandBrief: '展开', collapseBrief: '收起',
dismiss: '暂不需要', dismissed: '已忽略', replaced: '已替换',
unavailable: '暂时无法执行,请到插件页面查看',
transport: '暂时无法确认结果,请重新连接并查看卡片状态后再重试',
'row.title': '操作卡片', 'row.preparing': '正在准备卡片', 'row.running': '正在提议',
'row.ok': '已提议', 'row.error': '未显示', 'row.stopped': '已中断', 'row.inspect': '查看调用',
}
/** Japanese card frame, fallback and tool-row copy. */
export const ja: Record<ActionCardKey, string> = {
expandBrief: 'すべて表示', collapseBrief: '折りたたむ',
dismiss: '今はしない', dismissed: '却下済み', replaced: '置き換え済み',
unavailable: 'この操作は利用できません。プラグイン画面でプラグインを確認してください。',
transport: '結果を確認できませんでした。再接続してカードを確認してから再試行してください。',
'row.title': 'アクションカード', 'row.preparing': 'カードを準備中', 'row.running': '提案中',
'row.ok': '提案済み', 'row.error': '表示されませんでした', 'row.stopped': '中断されました', 'row.inspect': '呼び出しを確認',
}
/** Spanish card frame, fallback and tool-row copy. */
export const es: Record<ActionCardKey, string> = {
expandBrief: 'Mostrar más', collapseBrief: 'Mostrar menos',
dismiss: 'Ahora no', dismissed: 'Descartado', replaced: 'Reemplazado',
unavailable: 'Esta acción no está disponible. Revisa el complemento en Complementos.',
transport: 'No se pudo confirmar el resultado. Vuelve a conectarte y revisa la tarjeta antes de reintentar.',
'row.title': 'Tarjeta de acción', 'row.preparing': 'Preparando una tarjeta', 'row.running': 'Proponiendo',
'row.ok': 'Propuesto', 'row.error': 'No se mostró', 'row.stopped': 'Interrumpido', 'row.inspect': 'Inspeccionar llamada',
}
/** French card frame, fallback and tool-row copy. */
export const fr: Record<ActionCardKey, string> = {
expandBrief: 'Afficher plus', collapseBrief: 'Afficher moins',
dismiss: 'Pas maintenant', dismissed: 'Ignoré', replaced: 'Remplacé',
unavailable: "Cette action n'est pas disponible. Vérifiez le plugin dans Plugins.",
transport: 'Impossible de confirmer le résultat. Reconnectez-vous et vérifiez la carte avant de réessayer.',
'row.title': "Carte d'action", 'row.preparing': "Préparation d'une carte", 'row.running': 'Proposition en cours',
'row.ok': 'Proposé', 'row.error': 'Non affiché', 'row.stopped': 'Interrompu', 'row.inspect': "Inspecter l'appel",
}
/** German card frame, fallback and tool-row copy. */
export const de: Record<ActionCardKey, string> = {
expandBrief: 'Mehr anzeigen', collapseBrief: 'Weniger anzeigen',
dismiss: 'Nicht jetzt', dismissed: 'Verworfen', replaced: 'Ersetzt',
unavailable: 'Diese Aktion ist nicht verfügbar. Prüfen Sie das Plugin unter Plugins.',
transport: 'Das Ergebnis konnte nicht bestätigt werden. Verbinden Sie sich erneut und prüfen Sie die Karte, bevor Sie es erneut versuchen.',
'row.title': 'Aktionskarte', 'row.preparing': 'Karte wird vorbereitet', 'row.running': 'Wird vorgeschlagen',
'row.ok': 'Vorgeschlagen', 'row.error': 'Nicht angezeigt', 'row.stopped': 'Unterbrochen', 'row.inspect': 'Aufruf prüfen',
}
/** Russian card frame, fallback and tool-row copy. */
export const ru: Record<ActionCardKey, string> = {
expandBrief: 'Показать больше', collapseBrief: 'Показать меньше',
dismiss: 'Не сейчас', dismissed: 'Отклонено', replaced: 'Заменено',
unavailable: 'Это действие недоступно. Проверьте плагин в разделе «Плагины».',
transport: 'Не удалось подтвердить результат. Переподключитесь и проверьте карточку перед повторной попыткой.',
'row.title': 'Карточка действия', 'row.preparing': 'Подготовка карточки', 'row.running': 'Предлагается',
'row.ok': 'Предложено', 'row.error': 'Не показано', 'row.stopped': 'Прервано', 'row.inspect': 'Проверить вызов',
}
/** Portuguese card frame, fallback and tool-row copy. */
export const pt: Record<ActionCardKey, string> = {
expandBrief: 'Mostrar mais', collapseBrief: 'Mostrar menos',
dismiss: 'Agora não', dismissed: 'Dispensado', replaced: 'Substituído',
unavailable: 'Esta ação não está disponível. Verifique o plug-in em Plug-ins.',
transport: 'Não foi possível confirmar o resultado. Reconecte-se e verifique o cartão antes de tentar novamente.',
'row.title': 'Cartão de ação', 'row.preparing': 'Preparando um cartão', 'row.running': 'Propondo',
'row.ok': 'Proposto', 'row.error': 'Não exibido', 'row.stopped': 'Interrompido', 'row.inspect': 'Inspecionar chamada',
}
@@ -0,0 +1,63 @@
/** Register the card frame, the tool row, their copy and the body registry. */
import type { Context } from '@deepseek-ai/cordis'
import type { SessionId } from '@deepseek-ai/dsh-session/types'
import type {} from '@deepseek-ai/dsh-api-remotes/client'
import type {} from '@deepseek-ai/dsh-client-locale/client'
import type {} from '@deepseek-ai/dsh-client-ui-session/client'
import type {} from '@deepseek-ai/dsh-client-ui-renderer/client'
import type {} from '@deepseek-ai/dsh-client-ui-tool/client'
import type {} from '@deepseek-ai/dsh-action-cards/remote'
import type { TypertRemoteContribution } from '@deepseek-ai/dsh-typert-protocol'
import { ActionCardTail, type TailInjected } from './Card.tsx'
import { ProposeRow } from './ProposeRow.tsx'
import { ActionCardRegistry } from './registry.ts'
import { de, en, es, fr, ja, pt, ru, zh, type ActionCardKey } from './locales.ts'
declare module '@deepseek-ai/dsh-client-ui-slots' {
interface LocaleNamespaceMap { actionCards: ActionCardKey }
}
declare module '@deepseek-ai/cordis' {
interface Context {
/** Card bodies by action kind; kinds register theirs inside their own `ctx.effect`. */
actionCards: ActionCardRegistry
}
}
/** Registries available before this plugin mounts its Remote. */
export const inject = ['slots', 'locale', 'remote']
/**
* Provide the body registry, then mount the Remote and the UI for the same plugin lifetime.
* @param ctx - Client plugin context.
* @param contribution - Generated Remote descriptors supplied by the built entry.
* @returns disposer removing the UI before the Remote contribution.
*/
export async function mountActionCards(ctx: Context, contribution: TypertRemoteContribution): Promise<() => Promise<void>> {
// Built at the plugin's top level: kinds register into it from their own apply.
const registry = new ActionCardRegistry(ctx)
ctx.reflect.provide('actionCards', registry)
const unmount = await ctx.remote.$mount(contribution)
const ui = ctx.inject(['remote.actionCards'], (inner) => { registerUi(inner, registry) })
try { await ui } catch (error) { await ui.dispose(); await unmount(); throw error }
return async () => { await ui.dispose(); await unmount() }
}
function registerUi(ctx: Context, registry: ActionCardRegistry): void {
ctx.effect(() => ctx.locale.register('actionCards', { en, zh, ja, es, fr, de, ru, pt }), 'action-cards: locale')
ctx.slots.inject('conversation.chat.turnTail', () => ctx.slots.register({
name: 'conversation.chat.turnTail', id: '@deepseek-ai/dsh-client-ui-action-cards', locale: 'actionCards',
inject: (sessionId: SessionId): TailInjected => ({
sessionId,
hooks: { bodies: registry.source },
bind: locale => ctx.locale.bind(locale),
act: async (callId, verb, payload) => {
const result = await ctx.remote.actionCards.act(sessionId, callId, verb, payload)
return result.ok ? result.value : undefined
},
}),
}, ActionCardTail))
ctx.slots.inject('tool.call.toolview', () => ctx.slots.register(
{ name: 'tool.call.toolview', key: 'propose_action', locale: 'actionCards' }, ProposeRow,
))
}
@@ -0,0 +1,106 @@
/** Card bodies registered by action kind for the shared card frame. */
import { createElement, type ComponentType, type ReactNode } from 'react'
import type { Context } from '@deepseek-ai/cordis'
import type { HostObservable, LocaleNamespaceMap, Translate, TranslateNS } from '@deepseek-ai/dsh-client-ui-slots'
import type { SessionId } from '@deepseek-ai/dsh-session/types'
import type {
ActionCardDecision, ActionCardFields, ActionCardProposal, ActionCardResult,
} from '@deepseek-ai/dsh-action-cards/types'
/** Props of the frame's folded text, which bodies reuse for their own long fields. */
export interface FoldedTextProps {
readonly text: string
readonly expandLabel: string
readonly collapseLabel: string
}
/** Everything a kind's body receives from the shared frame. */
export interface ActionCardBodyProps<N extends Extract<keyof LocaleNamespaceMap, string>> {
/** The card as admitted; the body validates `details` with its own schema. */
readonly proposal: ActionCardProposal
/** Recorded or derived card state; undefined while the card is unstarted. */
readonly decision: ActionCardDecision | undefined
readonly sessionId: SessionId
/** Title and folded brief rendered by the frame; the body places them in its layout. */
readonly summary: ReactNode
/** The frame's width-weighted text fold. */
readonly Folded: ComponentType<FoldedTextProps>
/**
* Send one user action for this card.
* @param verb - `dismiss` or one of the kind's Host verbs.
* @param payload - fields the kind's Host handler validates.
* @returns the Host result, or undefined when the reply was lost.
*/
readonly act: (verb: string, payload?: ActionCardFields) => Promise<ActionCardResult | undefined>
/** Translate the body's own locale namespace. */
readonly t: TranslateNS<N>
}
/** Browser half of one action kind. */
export interface ActionCardDescriptor<N extends Extract<keyof LocaleNamespaceMap, string>> {
/** Value of the card's `action`. */
readonly action: string
/** Locale namespace the body's copy belongs to. */
readonly locale: N
/** Kind-owned header, controls, status and footer. */
readonly Body: ComponentType<ActionCardBodyProps<N>>
}
/** Frame-supplied body props before the kind's translator is bound. */
export type ActionCardFrameProps = Omit<ActionCardBodyProps<never>, 't'>
/** A registered body with its namespace type erased. */
export interface RegisteredBody {
readonly locale: string
/**
* Render the body.
* @param props - frame-supplied props.
* @param t - translator bound to {@link locale}.
* @returns the body element.
*/
readonly render: (props: ActionCardFrameProps, t: Translate) => ReactNode
}
/** Card bodies by action; the frame renders a dismiss-only fallback for an action without one. */
export class ActionCardRegistry {
private bodies: ReadonlyMap<string, RegisteredBody> = new Map()
private readonly listeners = new Set<() => void>()
/** Current bodies by action; the snapshot changes on every registration change. */
readonly source: HostObservable<ReadonlyMap<string, RegisteredBody>> = {
getSnapshot: () => this.bodies,
subscribe: (listener) => {
this.listeners.add(listener)
return () => { this.listeners.delete(listener) }
},
}
/** @param ctx - Context whose effects own registered bodies. */
constructor(private readonly ctx: Context) {}
/**
* Register one kind's body. The caller holds the returned disposer inside its own `ctx.effect`, so the body
* lives exactly as long as the plugin that contributed it.
* @param descriptor - the kind's action, locale namespace and body.
* @returns idempotent disposer.
* @throws when another body already serves the action.
*/
register<N extends Extract<keyof LocaleNamespaceMap, string>>(descriptor: ActionCardDescriptor<N>): () => void {
const { action, locale, Body } = descriptor
const body: RegisteredBody = { locale, render: (props, t) => createElement(Body, { ...props, t }) }
const dispose = this.ctx.effect(() => {
if (this.bodies.has(action)) throw new Error(`action-cards: a body for "${action}" is already registered`)
this.publish(new Map([...this.bodies, [action, body]]))
return () => {
const next = new Map(this.bodies)
next.delete(action)
this.publish(next)
}
}, `action-cards: body ${action}`)
return () => { void dispose() }
}
private publish(bodies: ReadonlyMap<string, RegisteredBody>): void {
this.bodies = bodies
for (const listener of [...this.listeners]) listener()
}
}
@@ -0,0 +1,4 @@
declare module '*.module.css' {
const classes: Readonly<Record<string, string>>
export default classes
}
@@ -0,0 +1,4 @@
/** Browser registration is discovered through the package client declaration. */
/** Register the browser-only contribution without Host state. */
export function apply(): void {}
@@ -0,0 +1,74 @@
/** Real registries bind actions to the displayed session and remove every contribution on unload. */
import { isValidElement } from 'react'
import { Context } from '@deepseek-ai/cordis'
import { SlotRegistry } from '@deepseek-ai/dsh-client-ui-renderer/client'
import { LocaleRuntime } from '@deepseek-ai/dsh-client-locale/client'
import { SessionId } from '@deepseek-ai/dsh-session/types'
import { ToolCallId } from '@deepseek-ai/dsh-llm/brand'
import { expect, it, onTestFinished, vi } from 'vitest'
import { mountActionCards, inject } from '../src/client/mount.ts'
import { apply as hostApply } from '../src/index.ts'
import type { TailInjected } from '../src/client/Card.tsx'
const apply = (ctx: Context) => mountActionCards(ctx, { package: '@deepseek-ai/dsh-action-cards', descriptors: [] })
const Body = () => null
it('binds actions to the displayed session, serves registered bodies and removes registrations on unload', async () => {
hostApply()
const ctx = new Context()
onTestFinished(() => ctx.fiber.dispose())
await ctx.plugin(SlotRegistry)
const slots = ctx.slots
slots.register({ name: 'root', children: {
'conversation.chat.turnTail': { kind: 'list', scope: 'session' },
'tool.call.toolview': { kind: 'keyed', scope: 'session' },
} }, (_props: { renderSlot?: unknown }) => null)
const act = vi.fn().mockResolvedValue({ ok: true, value: { status: 'accepted' } })
const unmount = vi.fn(async () => {})
ctx.provide('remote', { actionCards: { act }, $mount: vi.fn(async () => unmount) })
ctx.provide('remote.actionCards', { act })
const locale = new LocaleRuntime(ctx)
ctx.provide('locale', locale)
const fiber = await ctx.plugin({ inject, apply })
const entry = slots.entries('conversation.chat.turnTail')[0]!
expect(slots.entries('tool.call.toolview').map(item => item.options.key)).toEqual(['propose_action'])
const resolve = entry.inject as NonNullable<typeof entry.inject> & ((sessionId: SessionId) => TailInjected)
const bound = resolve(SessionId('session-one'))
expect(bound.sessionId).toBe('session-one')
expect(await bound.act(ToolCallId('card'), 'create', { slot: 'page' })).toEqual({ status: 'accepted' })
expect(act).toHaveBeenCalledWith('session-one', 'card', 'create', { slot: 'page' })
act.mockResolvedValueOnce({ ok: false })
expect(await bound.act(ToolCallId('card'), 'dismiss')).toBeUndefined()
expect(bound.bind('actionCards')('dismiss')).toBe('Not now')
const changes = vi.fn()
const unsubscribe = bound.hooks.bodies.subscribe(changes)
const kind = await ctx.plugin({ inject: ['actionCards'], apply: (inner: Context) => {
inner.effect(() => inner.actionCards.register({ action: 'test_action', locale: 'actionCards', Body }))
} })
expect([...bound.hooks.bodies.getSnapshot().keys()]).toEqual(['test_action'])
const translate = (key: string) => key
const element = bound.hooks.bodies.getSnapshot().get('test_action')!.render({ proposal: { callId: ToolCallId('card'), turn: 1,
action: 'test_action', title: 'Counter', brief: 'Keep count.', details: {} }, decision: undefined,
sessionId: SessionId('session-one'), summary: null, Folded: Body, act: vi.fn() }, translate)
expect(isValidElement(element) && element.type === Body && (element.props as { t: unknown }).t === translate).toBe(true)
expect(() => ctx.actionCards.register({ action: 'test_action', locale: 'actionCards', Body })).toThrow('a body for "test_action" is already registered')
await kind.dispose()
expect(bound.hooks.bodies.getSnapshot().size).toBe(0)
expect(changes).toHaveBeenCalledTimes(2)
unsubscribe()
const release = ctx.actionCards.register({ action: 'other_action', locale: 'actionCards', Body })
expect(bound.hooks.bodies.getSnapshot().has('other_action')).toBe(true)
release()
expect(bound.hooks.bodies.getSnapshot().has('other_action')).toBe(false)
expect(changes).toHaveBeenCalledTimes(2)
await fiber.dispose()
expect(unmount).toHaveBeenCalledOnce()
expect(slots.entries('conversation.chat.turnTail')).toHaveLength(0)
expect(slots.entries('tool.call.toolview')).toHaveLength(0)
expect(ctx.get('actionCards')).toBeUndefined()
vi.spyOn(locale, 'register').mockImplementation(() => { throw new Error('registration failed') })
const failed = ctx.plugin({ inject, apply })
await expect(failed.await()).rejects.toThrow('registration failed')
await failed.dispose()
expect(unmount).toHaveBeenCalledTimes(2)
})
@@ -0,0 +1,141 @@
// @vitest-environment jsdom
import { memo, type ComponentProps } from 'react'
import { act, cleanup, fireEvent, render, screen } from '@testing-library/react'
import { afterEach, expect, it, vi } from 'vitest'
import { ToolCallId } from '@deepseek-ai/dsh-llm/brand'
import { SessionId, SessionSeq } from '@deepseek-ai/dsh-session/types'
import type { ActionCardDecision, ActionCardResult, ActionCardView } from '@deepseek-ai/dsh-action-cards/types'
import { ActionCard, ActionCardTail, FoldedText } from '../src/client/Card.tsx'
import { makeTranslate } from '@deepseek-ai/dsh-client-test-runtime'
import { en, zh } from '../src/client/locales.ts'
import type { ActionCardBodyProps, RegisteredBody } from '../src/client/registry.ts'
afterEach(() => { cleanup(); vi.restoreAllMocks(); vi.unstubAllGlobals() })
type CardProps = ComponentProps<typeof ActionCard>
const callId = ToolCallId('card-1')
const proposal = { callId, turn: 3, action: 'test_action', title: 'Counter', brief: 'Keep my task count visible.', details: { value: 'a' } }
function props(bodies: ReadonlyMap<string, RegisteredBody> = new Map(), decision?: ActionCardDecision, chinese = false): CardProps {
return { proposal, decision, sessionId: SessionId('session-1'),
act: vi.fn(async (): Promise<ActionCardResult | undefined> => ({ status: 'settled', decision: { callId, action: 'test_action', phase: 'dismissed' } })),
bind: vi.fn(() => (key: string) => `body:${key}`),
useBodies: selector => selector(bodies),
t: makeTranslate(chinese ? zh : en) }
}
it('places the card admitted in its turn once the turn has ended with an answer, and leaves other turns empty', () => {
type Tail = ComponentProps<typeof ActionCardTail>
const value = props()
let view: ActionCardView | undefined
const turnData = vi.fn().mockReturnValue({ closing: {} })
const ended = { type: 'turn/end', seq: SessionSeq(1), time: 0, data: { turn: 3, reason: { kind: 'max-tokens' } } } as const
const tail = (end: typeof ended | undefined): Tail => ({ ...value, useProjection: () => view,
turn: { turn: 3, start: undefined, end, status: end === undefined ? 'open' : 'closed', steps: [],
data: { get: turnData, source: () => ({ getSnapshot: () => undefined, subscribe: () => () => {} }) } } })
const card = (id: string, turn: number) => ({ ...proposal, callId: ToolCallId(id), turn })
const { rerender } = render(<ActionCardTail {...tail(ended)} />)
expect(screen.queryByRole('region')).toBeNull()
view = { decisions: {}, proposals: { earlier: card('earlier', 2) } }
rerender(<ActionCardTail {...tail(ended)} />)
expect(screen.queryByRole('region')).toBeNull()
view = { decisions: {}, proposals: { ...view.proposals, b: card('b', 3), a: card('a', 3), c: card('c', 3) } }
rerender(<ActionCardTail {...tail(ended)} />)
expect(screen.getAllByRole('region').map(region => region.getAttribute('data-action-card'))).toEqual(['a'])
expect(screen.getByRole('region', { name: 'Counter' }).getAttribute('data-action')).toBe('test_action')
expect(turnData).toHaveBeenCalledWith('turn-tail')
rerender(<ActionCardTail {...tail(undefined)} />)
expect(screen.queryByRole('region')).toBeNull()
turnData.mockReturnValueOnce({ closing: null })
rerender(<ActionCardTail {...tail(ended)} />)
expect(screen.queryByRole('region')).toBeNull()
view = { ...view, decisions: { a: { callId: ToolCallId('a'), action: 'test_action', phase: 'dismissed' } } }
rerender(<ActionCardTail {...tail(ended)} />)
expect(screen.getByRole('status').textContent).toBe(en.dismissed)
// A call id naming an inherited object member reads no decision.
view = { decisions: {}, proposals: { toString: card('toString', 3) } }
rerender(<ActionCardTail {...tail(ended)} />)
expect(screen.getByRole('region').getAttribute('data-action-card')).toBe('toString')
expect(screen.getByRole('button', { name: en.dismiss })).toBeTruthy()
})
it('renders a registered body with the frame summary, the fold, its own translator and an action bound to the card', async () => {
const seen: ActionCardBodyProps<never>[] = []
const Body = memo((body: ActionCardBodyProps<never>) => {
seen.push(body)
return <div>{body.summary}<button type="button" onClick={() => { void body.act('start', { value: 'x' }) }}>{body.t('start' as never)}</button>
<body.Folded text="Long details" expandLabel="more" collapseLabel="less" /></div>
})
const bodies = new Map<string, RegisteredBody>([['test_action', { locale: 'testCard', render: (frame, t) => <Body {...frame} t={t} /> }]])
const value = props(bodies, { callId, action: 'test_action', phase: 'running' })
render(<ActionCard {...value} />)
expect(value.bind).toHaveBeenCalledWith('testCard')
expect(screen.getByRole('region', { name: 'Counter' }).textContent).toContain('Keep my task count visible.')
expect(screen.getByText('Long details')).toBeTruthy()
expect(seen.at(-1)).toMatchObject({ proposal, decision: { phase: 'running' }, sessionId: 'session-1', Folded: FoldedText })
fireEvent.click(screen.getByRole('button', { name: 'body:start' }))
expect(value.act).toHaveBeenCalledWith(callId, 'start', { value: 'x' })
})
it('offers only Not now for a card whose kind registered no body, and reports refusals and lost replies', async () => {
const value = props(new Map(), undefined, true)
const pending = Promise.withResolvers<ActionCardResult | undefined>()
value.act = vi.fn().mockReturnValueOnce(pending.promise).mockResolvedValueOnce(undefined)
.mockRejectedValueOnce(new Error('disconnected')).mockResolvedValueOnce({ status: 'unconfirmed', reason: 'closing' })
.mockResolvedValueOnce({ status: 'accepted' })
render(<ActionCard {...value} />)
expect(screen.getByRole('region', { name: 'Counter' }).textContent).toContain('Keep my task count visible.')
const dismiss = screen.getByRole('button', { name: zh.dismiss })
fireEvent.click(dismiss)
expect(dismiss.hasAttribute('disabled')).toBe(true)
expect(value.act).toHaveBeenCalledWith(callId, 'dismiss', undefined)
await act(async () => { pending.resolve({ status: 'refused', reason: 'unknown' }) })
expect(screen.getByRole('alert').textContent).toBe(zh.unavailable)
for (const text of [zh.transport, zh.transport, zh.unavailable]) {
fireEvent.click(screen.getByRole('button', { name: zh.dismiss }))
await vi.waitFor(() => { expect(screen.getByRole('alert').textContent).toBe(text) })
}
fireEvent.click(screen.getByRole('button', { name: zh.dismiss }))
await vi.waitFor(() => { expect(screen.queryByRole('alert')).toBeNull() })
})
it('dismisses a card through the fallback Not now when the Host records the dismissal without the card kind', async () => {
const value = props()
render(<ActionCard {...value} />)
const dismiss = screen.getByRole('button', { name: en.dismiss })
fireEvent.click(dismiss)
expect(value.act).toHaveBeenCalledWith(callId, 'dismiss', undefined)
await vi.waitFor(() => { expect(dismiss.hasAttribute('disabled')).toBe(false) })
expect(screen.queryByRole('alert')).toBeNull()
})
it.each([['dismissed', en.dismissed], ['replaced', en.replaced], ['finished', undefined], ['toString', undefined]] as const)(
'shows the fallback status of a %s card without actions', (phase, text) => {
render(<ActionCard {...props(new Map(), { callId, action: 'test_action', phase })} />)
expect(screen.queryByRole('status')?.textContent).toBe(text)
expect(screen.queryByRole('button', { name: en.dismiss })).toBeNull()
})
it('only offers expansion for omitted text and preserves the full brief without layout measurement', () => {
vi.stubGlobal('ResizeObserver', undefined)
const short = 'Keep my task count visible.\n\nPersist across conversations.\nVerify removal.'
const view = render(<FoldedText text={short} expandLabel={zh.expandBrief} collapseLabel={zh.collapseBrief} />)
expect(screen.queryByRole('button', { name: zh.expandBrief })).toBeNull()
expect(screen.getByText(/Keep my task count visible/).textContent).toBe(short)
// 129 wide graphemes (258 units) plus one narrow emoji fill the 260-unit budget; the next grapheme folds.
const prefix = '计'.repeat(129) + '👩🏽‍💻'
const long = prefix + '\n\n保留完整方案,点击创建后交给 Creator。'
view.rerender(<FoldedText text={long} expandLabel={zh.expandBrief} collapseLabel={zh.collapseBrief} />)
const expand = screen.getByRole('button', { name: zh.expandBrief })
const paragraph = document.getElementById(expand.getAttribute('aria-controls')!)!
expect(paragraph.textContent).toBe(prefix + '…')
expect(expand.getAttribute('aria-expanded')).toBe('false')
fireEvent.click(expand)
expect(screen.getByRole('button', { name: zh.collapseBrief }).getAttribute('aria-expanded')).toBe('true')
expect(paragraph.textContent).toBe(long)
fireEvent.click(screen.getByRole('button', { name: zh.collapseBrief }))
expect(paragraph.textContent).toBe(prefix + '…')
view.rerender(<FoldedText text={prefix} expandLabel={zh.expandBrief} collapseLabel={zh.collapseBrief} />)
expect(screen.queryByRole('button', { name: zh.expandBrief })).toBeNull()
expect(paragraph.textContent).toBe(prefix)
})
@@ -0,0 +1,55 @@
// @vitest-environment jsdom
/** The process row reads the card title from the recorded arguments and expands only a refused call. */
import { cleanup, fireEvent, render } from '@testing-library/react'
import { afterEach, expect, it, vi } from 'vitest'
import type { StartedToolCall, ToolResultNode } from '@deepseek-ai/dsh-client-ui-chat/client'
import { makeTranslate } from '@deepseek-ai/dsh-client-test-runtime'
import { PartialArguments } from '@deepseek-ai/dsh-util-values'
import { ProposeRow } from '../src/client/ProposeRow.tsx'
import { en } from '../src/client/locales.ts'
afterEach(cleanup)
type Props = Parameters<typeof ProposeRow>[0]
const argsRaw = JSON.stringify({ action: 'test_action', title: 'Counter', brief: 'Keep count.', details: {} })
const running: StartedToolCall = { phase: 'start', args: PartialArguments.fromText(argsRaw), callId: 'c', name: 'propose_action', argsRaw, turn: 1, step: 1, time: 1, subCalls: [] }
const settled: ToolResultNode = { kind: 'tool-result', seq: 2, time: 2, callId: 'c', name: 'propose_action', args: PartialArguments.fromText(argsRaw),
call: { name: 'propose_action', argsRaw }, callTime: 1, content: [{ type: 'text', text: 'Card shown below your answer. Do not restate it. Invite the user.' }], isError: false, subCalls: [] }
function props(block: Props['block'], inspect?: () => void): Props {
return { ...('kind' in block ? { phase: 'result' as const, block } : { phase: block.phase, block }),
callId: 'c', toolName: 'propose_action', openFile: vi.fn(), inspect, t: makeTranslate(en) } as Props
}
it('shows a proposed card on one line without exposing the model-facing result', () => {
const view = render(<ProposeRow {...props(settled)} />)
expect(view.container.querySelector('[data-tool="propose_action"]')?.getAttribute('data-state')).toBe('ok')
expect(view.getByText('Proposed')).toBeTruthy()
expect(view.getByText('Counter')).toBeTruthy()
expect(view.queryByRole('button')).toBeNull()
expect(view.container.textContent).not.toContain('Card shown below your answer. Do not restate it. Invite the user.')
})
it('explains a refused call and offers call inspection', () => {
const inspect = vi.fn()
const view = render(<ProposeRow {...props({ ...settled, isError: true,
content: [{ type: 'text', text: 'Error: Load skill test-skill first, then call propose_action.' }, { type: 'reasoning', text: 'hidden' }] }, inspect)} />)
expect(view.getByText('Not shown')).toBeTruthy()
const row = view.getByRole('button')
fireEvent.click(row)
expect(view.container.querySelector('pre')?.textContent).toBe('Error: Load skill test-skill first, then call propose_action.')
fireEvent.click(view.getByRole('button', { name: 'Inspect call' }))
expect(inspect).toHaveBeenCalledOnce()
fireEvent.click(row)
expect(row.getAttribute('aria-expanded')).toBe('false')
})
it.each([
['preparing', { phase: 'preparing', args: PartialArguments.EMPTY, callId: 'c', name: 'propose_action', turn: 1, step: 1, time: 1, subCalls: [] }, 'Preparing a card'],
['running', running, 'Proposing'],
['stopped', { ...settled, isError: true, content: [], error: { name: 'Interrupted', code: 'interrupted' } }, 'Interrupted'],
] as const)('renders the %s call stage without claiming a card', (state, block, label) => {
const view = render(<ProposeRow {...props(block)} />)
expect(view.container.querySelector('[data-tool="propose_action"]')?.getAttribute('data-state')).toBe(state)
expect(view.getAllByText(label).length).toBeGreaterThan(0)
expect(view.queryByText('Proposed')).toBeNull()
expect(view.queryByRole('button')).toBeNull()
})
@@ -0,0 +1,27 @@
{
"extends": "../../../tsconfig.base.client.json",
"compilerOptions": {
"rootDir": "src",
"outDir": "lib/types"
},
"include": [
"src"
],
"references": [
{ "path": "../../client/ui-renderer" },
{ "path": "../../../vendor/cordis" },
{ "path": "../../api/remotes/tsconfig.client.json" },
{ "path": "../../client/locale/tsconfig.client.json" },
{ "path": "../../client/ui-conversation/tsconfig.client.json" },
{ "path": "../../client/ui-chat/tsconfig.client.json" },
{ "path": "../../client/ui-session" },
{ "path": "../../client/ui-slots" },
{ "path": "../../client/ui-primitives" },
{ "path": "../../client/ui-tool" },
{ "path": "../../llm/llm" },
{ "path": "../../core/session" },
{ "path": "../action-cards" },
{ "path": "../../typert/protocol" },
{ "path": "../../util/values" }
]
}
Loaded 100 of 254 files, more files were not shown because too many files have changed in this diff. Show more