Merge feat/creation-recommendations into plugin recommendation experiment
This commit is contained in:
commit
e11ce354c1
347 files changed
+5119
-2935
No files matched your search
@@ -234,6 +234,12 @@ describe('client bundle purity gate', () => {
|
||||
expect(() => resolveId('@deepseek-ai/dsh-spill-policy/notice/internal')).toThrow(/purity/)
|
||||
})
|
||||
|
||||
it('admits only the pure proposal block parser of the creation experiment, not its Host plugin', () => {
|
||||
expect(resolveId('@deepseek-ai/dsh-experimental-creation-recommendations/action-block')).toBeNull()
|
||||
expect(() => resolveId('@deepseek-ai/dsh-experimental-creation-recommendations')).toThrow(/purity/)
|
||||
expect(() => resolveId('@deepseek-ai/dsh-experimental-creation-recommendations/action-block/internal')).toThrow(/purity/)
|
||||
})
|
||||
|
||||
it('inlines the browser-safe Timer entry while sharing Cordis through the module table', () => {
|
||||
const timer = '@deepseek-ai/cordis-plugin-timer'
|
||||
expect(resolveId(timer)).toBeNull()
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
/** Raw retention and candidate evaluation preserve the running plugin's config identity. */
|
||||
import { Context, type Fiber } from '@deepseek-ai/cordis'
|
||||
import Loader from '@deepseek-ai/cordis-plugin-loader'
|
||||
import z from '@deepseek-ai/schemastery'
|
||||
import { describe, expect, it, onTestFinished, vi } from 'vitest'
|
||||
|
||||
function root(): Context {
|
||||
const ctx = new Context()
|
||||
onTestFinished(() => ctx.fiber.dispose())
|
||||
return ctx
|
||||
}
|
||||
|
||||
describe('Fiber configuration access', () => {
|
||||
it('keeps raw and running references unchanged while resolving valid or invalid candidates', async () => {
|
||||
const ctx = root()
|
||||
await ctx.plugin(Loader)
|
||||
const Config = z.object({ value: z.number().min(0).volatile() })
|
||||
const applied = vi.fn()
|
||||
ctx.loader.builtins.consumer = { Config, apply: applied }
|
||||
const raw = { value: { __jsExpr: '1 + 1' } }
|
||||
const id = await ctx.loader.create({ name: 'cordis:consumer', config: raw })
|
||||
const fiber = ctx.loader.resolve(id).fiber!
|
||||
await fiber.await()
|
||||
const running = applied.mock.calls[0]![1] as ReturnType<typeof Config>
|
||||
expect(fiber.rawConfig).toBe(raw)
|
||||
expect(fiber.effectiveConfig).toBe(running)
|
||||
const candidate = fiber.resolveConfig({ value: { __jsExpr: '2 + 2' } }) as ReturnType<typeof Config>
|
||||
expect(candidate.value.get()).toBe(4)
|
||||
expect(candidate.value).not.toBe(running.value)
|
||||
expect(() => fiber.resolveConfig({ value: -1 })).toThrow()
|
||||
expect(fiber.rawConfig).toBe(raw)
|
||||
expect(fiber.effectiveConfig).toBe(running)
|
||||
expect(running.value.get()).toBe(2)
|
||||
expect(applied).toHaveBeenCalledOnce()
|
||||
})
|
||||
|
||||
it('uses the canonical fiber through registry wrappers and retains raw input without updating', async () => {
|
||||
const ctx = root()
|
||||
const initial = { value: 1 }
|
||||
const apply = vi.fn()
|
||||
const wrapper = ctx.plugin(apply, initial)
|
||||
await wrapper
|
||||
const fiber = wrapper.ctx.fiber
|
||||
const hook = vi.fn(function (this: Fiber, _config: unknown, next: () => unknown) {
|
||||
expect(this).toBe(fiber)
|
||||
return next()
|
||||
})
|
||||
fiber.ctx.on('internal/config', hook)
|
||||
const raw = { value: 2 }
|
||||
wrapper.rawConfig = raw
|
||||
expect(fiber.rawConfig).toBe(raw)
|
||||
expect(wrapper.effectiveConfig).toBe(initial)
|
||||
expect(wrapper.resolveConfig(raw)).toBe(raw)
|
||||
expect(hook).toHaveBeenCalledOnce()
|
||||
expect(fiber.effectiveConfig).toBe(initial)
|
||||
expect(apply).toHaveBeenCalledOnce()
|
||||
await wrapper.restart()
|
||||
expect(fiber.effectiveConfig).toBe(raw)
|
||||
expect(apply).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
|
||||
it('publishes updates through registry wrappers to the canonical fiber', async () => {
|
||||
const ctx = root()
|
||||
const initial = { value: 1 }
|
||||
const apply = vi.fn()
|
||||
const wrapper = ctx.plugin(apply, initial)
|
||||
await wrapper
|
||||
const raw = { value: 2 }
|
||||
wrapper.update(raw)
|
||||
await wrapper.await()
|
||||
expect(apply).toHaveBeenLastCalledWith(wrapper.ctx, raw)
|
||||
expect(wrapper.rawConfig).toBe(raw)
|
||||
expect(wrapper.effectiveConfig).toBe(raw)
|
||||
expect(wrapper.ctx.fiber.rawConfig).toBe(raw)
|
||||
expect(wrapper.ctx.fiber.effectiveConfig).toBe(raw)
|
||||
expect(apply).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
|
||||
it('retains a pending raw update until its injection is available', async () => {
|
||||
const ctx = root()
|
||||
ctx.provide('configAccessTest')
|
||||
const apply = vi.fn()
|
||||
const wrapper = ctx.plugin({ inject: ['configAccessTest'], apply }, { value: 1 })
|
||||
const raw = { value: 2 }
|
||||
wrapper.update(raw)
|
||||
expect(wrapper.rawConfig).toBe(raw)
|
||||
expect(wrapper.effectiveConfig).toBeUndefined()
|
||||
ctx.set('configAccessTest', {})
|
||||
await wrapper
|
||||
expect(wrapper.effectiveConfig).toBe(raw)
|
||||
expect(apply).toHaveBeenCalledOnce()
|
||||
})
|
||||
})
|
||||
@@ -1,27 +1,27 @@
|
||||
/** Add a native tool after a file read and unregister it after its first execution. */
|
||||
/** Activate a native tool after a file read and disable it after its first execution. */
|
||||
export const name = 'python-sdk-dynamic-tools'
|
||||
export const inject = ['tools']
|
||||
|
||||
/** @param {import('@deepseek-ai/cordis').Context} ctx - Scenario-owned tool registry. */
|
||||
export function apply(ctx) {
|
||||
let removeTool
|
||||
let active = false
|
||||
ctx.tools.register({
|
||||
name: 'snapshot_ping',
|
||||
description: 'Return pong once.',
|
||||
parameters: { type: 'object', properties: {}, additionalProperties: false },
|
||||
output: {
|
||||
schema: { type: 'string' },
|
||||
render(_args, value) { return [{ type: 'text', text: value }] },
|
||||
},
|
||||
async execute() { return 'pong' },
|
||||
}, { when: async ({ modelSelection }) => active && modelSelection.provider === 'deepseek-official' })
|
||||
ctx.on('tools/post-execute', async (exec, result, next) => {
|
||||
const downstream = await next()
|
||||
if (result.isError) return downstream
|
||||
if (exec.name === 'read' && exec.arguments.file_path === 'dynamic-tool-task.txt') {
|
||||
removeTool = ctx.effect(() => ctx.tools.register({
|
||||
name: 'snapshot_ping',
|
||||
description: 'Return pong once.',
|
||||
parameters: { type: 'object', properties: {}, additionalProperties: false },
|
||||
output: {
|
||||
schema: { type: 'string' },
|
||||
render(_args, value) { return [{ type: 'text', text: value }] },
|
||||
},
|
||||
async execute() { return 'pong' },
|
||||
}))
|
||||
active = true
|
||||
} else if (exec.name === 'snapshot_ping') {
|
||||
removeTool?.()
|
||||
removeTool = undefined
|
||||
active = false
|
||||
}
|
||||
return downstream
|
||||
})
|
||||
|
||||
@@ -468,6 +468,8 @@ export const LINK_MAP: Readonly<Record<string, string>> = {
|
||||
PromptFileBinding: 'attachment.md',
|
||||
FileUploadReceiptId: 'attachment.md',
|
||||
FileUploadValue: 'attachment.md',
|
||||
ImageUploadValue: 'attachment.md',
|
||||
StagedAttachment: 'attachment.md',
|
||||
SessionStartSource: 'core.md',
|
||||
ToolCallId: 'core.md',
|
||||
SessionLogSnapshot: 'session-query.md',
|
||||
@@ -758,6 +760,9 @@ export const LINK_MAP: Readonly<Record<string, string>> = {
|
||||
ToolCallResult: 'tools.md',
|
||||
ToolBatchResult: 'tools.md',
|
||||
ToolRestriction: 'tools.md',
|
||||
ToolActivationContext: 'tools.md',
|
||||
ToolRegistrationOptions: 'tools.md',
|
||||
ResolvedToolSet: 'tools.md',
|
||||
ToolSchema: 'tools.md',
|
||||
SettingsNamespace: 'settings.md',
|
||||
SettingsDescriptor: 'settings.md',
|
||||
@@ -990,6 +995,7 @@ export const TYPE_LINK_EXEMPTIONS: Readonly<Record<string, string>> = {
|
||||
JsonValue: 'JSON value union is owned by packages/core/session/src/json.ts',
|
||||
KnobState: 'projection unit state fields are owned by packages/interaction/permission-presets/README.md',
|
||||
PromptAssembly: 'assembly result is owned by packages/core/system-prompt/README.md',
|
||||
PromptPresentation: 'presentation inputs are owned by packages/core/system-prompt/README.md',
|
||||
RequestRunId: 'dynamic-package payload contract is owned by packages/extensions/cordis-host-runner/src/types.ts',
|
||||
RpcReceipt: 'carrier-layer receipt is owned by packages/client/connection/src/rpc.ts',
|
||||
SessionForkSource: 'service-local fork input is owned by packages/core/session/src/index.ts',
|
||||
|
||||
@@ -1593,7 +1593,8 @@ function renderLifecycle(): string {
|
||||
' Note over Agent,Driver: claim pending next-step input plus one queued prompt',
|
||||
` Driver-->>SDK: ${mermaidCode('agent/inbox/spliced')} pure deletion`,
|
||||
` Driver-->>SDK: ${mermaidCode('agent/inbox/claimed')} { message, turn } per message`,
|
||||
` Driver->>Prompt: ${mermaidCode('system-prompt/assemble')} waterfall`,
|
||||
' Driver->>Tools: resolve({ agent, modelSelection, signal }) once per proposed step',
|
||||
` Driver->>Prompt: ${mermaidCode('system-prompt/assemble')} waterfall with the step tool set`,
|
||||
` Driver->>Hooks: ${mermaidCode('agent/pre-step')} waterfall`,
|
||||
' Hooks-->>Driver: authoritative reject or enter(messages)',
|
||||
' alt proposed step rejected, first batch empty, or pre-step failed',
|
||||
@@ -1633,6 +1634,7 @@ function renderLifecycle(): string {
|
||||
' end',
|
||||
' end',
|
||||
` Driver->>Session: ${mermaidCode('step/end')}`,
|
||||
' Driver->>Tools: dispose the step tool set',
|
||||
' opt natural stop and next-step inbox empty',
|
||||
` Driver->>Hooks: ${mermaidCode('agent/turn-stopping')} serial terminal checkpoint`,
|
||||
' end',
|
||||
|
||||
@@ -47,9 +47,9 @@ describe('volatile Loader updates', () => {
|
||||
const fiber = await mountEntry(ctx, { Config, apply(owner: Context) {
|
||||
owner.on('loader/volatile-update', events)
|
||||
} }, { title: 'first' })
|
||||
const reference = fiber.config as ReturnType<typeof Config>
|
||||
const reference = fiber.effectiveConfig as ReturnType<typeof Config>
|
||||
await update(fiber, { title: 'second' })
|
||||
expect(fiber.config).toBe(reference)
|
||||
expect(fiber.effectiveConfig).toBe(reference)
|
||||
expect(reference.get()).toEqual({ title: 'second' })
|
||||
expect(events).toHaveBeenCalledExactlyOnceWith([[]])
|
||||
})
|
||||
@@ -70,7 +70,7 @@ describe('volatile Loader updates', () => {
|
||||
await update(first, { title: 'next', nested: { count: 2 } })
|
||||
await first.await()
|
||||
expect(mount).toHaveBeenCalledTimes(2)
|
||||
expect(first.config).toBe(initial)
|
||||
expect(first.effectiveConfig).toBe(initial)
|
||||
expect(initial.title).toBe(title)
|
||||
expect(configs[1]!.title.get()).toBe('hello')
|
||||
expect(observed).toEqual([{ paths: [['title'], ['nested', 'count']], title: 'next', count: 2 }])
|
||||
@@ -117,9 +117,9 @@ describe('volatile Loader updates', () => {
|
||||
hooks.push(config.title.get())
|
||||
})
|
||||
} }, {})
|
||||
const initial = fiber.config as ReturnType<typeof Config>
|
||||
const initial = fiber.effectiveConfig as ReturnType<typeof Config>
|
||||
await update(fiber, { title: 'accepted' })
|
||||
expect(fiber.config).toBe(initial)
|
||||
expect(fiber.effectiveConfig).toBe(initial)
|
||||
expect(initial.title.get()).toBe('accepted')
|
||||
await update(fiber, { title: 'accepted' })
|
||||
expect(hooks).toEqual([])
|
||||
@@ -180,11 +180,11 @@ describe('volatile Loader updates', () => {
|
||||
const oldUrl = 'https://a.example/'
|
||||
const newUrl = 'https://b.example/'
|
||||
const fiber = await mountEntry(ctx, { Config: schema, apply }, { live: 'a', url: oldUrl })
|
||||
const before = fiber.config as ReturnType<typeof schema>
|
||||
const before = fiber.effectiveConfig as ReturnType<typeof schema>
|
||||
await update(fiber, { live: 'b', url: newUrl })
|
||||
await fiber.await()
|
||||
expect(apply).toHaveBeenCalledTimes(2)
|
||||
expect((fiber.config as ReturnType<typeof schema>).url.href).toBe(newUrl)
|
||||
expect((fiber.effectiveConfig as ReturnType<typeof schema>).url.href).toBe(newUrl)
|
||||
expect(before.live.get()).toBe('a')
|
||||
await update(fiber, { live: 'c', url: newUrl, data: 'first' })
|
||||
await update(fiber, { live: 'd', url: newUrl, data: 'second' })
|
||||
@@ -199,7 +199,7 @@ describe('volatile Loader updates', () => {
|
||||
owner.on('loader/volatile-update', events)
|
||||
} }, { values: [undefined] })
|
||||
await update(fiber, { values: [null] })
|
||||
expect((fiber.config as ReturnType<typeof schema>).values.get()).toEqual([null])
|
||||
expect((fiber.effectiveConfig as ReturnType<typeof schema>).values.get()).toEqual([null])
|
||||
expect(events).toHaveBeenCalledExactlyOnceWith([['values']])
|
||||
})
|
||||
})
|
||||
@@ -308,7 +308,7 @@ it('updates volatile children through Group and Include custom updates and saves
|
||||
expect(includeEntry.fiber).toBe(originalInclude)
|
||||
expect(group.fiber).toBe(originalGroup)
|
||||
expect(consumer.fiber).toBe(originalConsumer)
|
||||
expect(consumer.fiber!.config).toBe(initial)
|
||||
expect(consumer.fiber!.effectiveConfig).toBe(initial)
|
||||
expect(configs).toHaveLength(1)
|
||||
expect(notifications).toHaveBeenCalledTimes(2)
|
||||
expect(childUpdates).not.toHaveBeenCalled()
|
||||
@@ -318,7 +318,7 @@ it('updates volatile children through Group and Include custom updates and saves
|
||||
entry.fiber!.update({ title: 'custom-update', fixed: 'ordinary' })
|
||||
expect(childUpdates).toHaveBeenCalledOnce()
|
||||
expect(entry.options.config).toEqual({ title: 'custom-update', fixed: 'ordinary' })
|
||||
expect((entry.fiber!.config as ReturnType<typeof Config>).title.get()).toBe('hello')
|
||||
expect((entry.fiber!.effectiveConfig as ReturnType<typeof Config>).title.get()).toBe('hello')
|
||||
entry.fiber!.update({ title: 'ephemeral', fixed: 'ordinary' }, true)
|
||||
expect(childUpdates).toHaveBeenCalledTimes(2)
|
||||
expect(entry.options.config).toEqual({ title: 'custom-update', fixed: 'ordinary' })
|
||||
@@ -332,11 +332,11 @@ it('commits volatile edits in place and retains direct Fiber updates', async ()
|
||||
const notifications = vi.fn()
|
||||
const apply = vi.fn(function (owner: Context) { owner.on('loader/volatile-update', notifications) })
|
||||
const fiber = await mountEntry(ctx, { Config, apply }, {})
|
||||
const original = fiber.config as ReturnType<typeof Config>
|
||||
const original = fiber.effectiveConfig as ReturnType<typeof Config>
|
||||
await ctx.loader.create({ name: 'cordis:disabled', disabled: true })
|
||||
await update(fiber, { title: 'enabled' })
|
||||
expect(apply).toHaveBeenCalledOnce()
|
||||
expect(fiber.config).toBe(original)
|
||||
expect(fiber.effectiveConfig).toBe(original)
|
||||
expect(original.title.get()).toBe('enabled')
|
||||
expect(notifications).toHaveBeenCalledExactlyOnceWith([['title']])
|
||||
await update(fiber, { title: 'invalid', nested: { count: -1 } })
|
||||
@@ -344,7 +344,7 @@ it('commits volatile edits in place and retains direct Fiber updates', async ()
|
||||
await update(fiber, { title: 'mixed', fixed: 'changed' })
|
||||
expect(apply).toHaveBeenCalledTimes(2)
|
||||
expect(original.title.get()).toBe('enabled')
|
||||
const current = fiber.config as ReturnType<typeof Config>
|
||||
const current = fiber.effectiveConfig as ReturnType<typeof Config>
|
||||
expect(current.title.get()).toBe('mixed')
|
||||
|
||||
const hook = vi.fn((_config: unknown, _noSave: boolean, next: () => void) => { next() })
|
||||
@@ -365,7 +365,7 @@ it('parses a volatile-only update through the config hook without patching the e
|
||||
const fiber = await mountEntry(ctx, { Config, apply(owner: Context) {
|
||||
owner.on('loader/volatile-update', () => { trace.push('notify') })
|
||||
} }, { block: { title: 'initial' } })
|
||||
const current = fiber.config as ReturnType<typeof Config>
|
||||
const current = fiber.effectiveConfig as ReturnType<typeof Config>
|
||||
const resolveHook = vi.fn((_raw: unknown, next: () => unknown) => next())
|
||||
fiber.ctx.on('internal/config', resolveHook)
|
||||
const contextPatch = vi.fn((_entry: unknown, next: () => void) => { next() })
|
||||
@@ -379,7 +379,7 @@ it('parses a volatile-only update through the config hook without patching the e
|
||||
expect(resolveHook).toHaveBeenCalledOnce()
|
||||
expect(trace).toEqual(['validate', 'notify'])
|
||||
expect(current.block.get()).toEqual({ title: 'changed' })
|
||||
expect(fiber._config).toBe(raw)
|
||||
expect(fiber.rawConfig).toBe(raw)
|
||||
})
|
||||
|
||||
it.each([true, false])('keeps old references untouched when isolation replaces a dependency (available: %s)', async (available) => {
|
||||
@@ -452,7 +452,7 @@ it.each(['group', 'include'] as const)('applies profile patches to a %s subtree
|
||||
await reconcileProfilePatches(ctx, patch('second'), 'volatile-test')
|
||||
expect(parent.fiber).toBe(parentFiber)
|
||||
expect(entry.fiber).toBe(childFiber)
|
||||
expect(entry.fiber!.config).toBe(original)
|
||||
expect(entry.fiber!.effectiveConfig).toBe(original)
|
||||
expect(original.title).toBe(reference)
|
||||
expect(reference.get()).toBe('second')
|
||||
expect(configs).toHaveLength(1)
|
||||
@@ -481,9 +481,9 @@ it('logs an invalid candidate and keeps its references while other entries commi
|
||||
await invalid.entry!.update({ config: { title: 'invalid', nested: { count: -1 } } })
|
||||
await healthy.entry!.update({ config: { title: 'healthy' } })
|
||||
expect(warning).toHaveBeenCalledTimes(2)
|
||||
expect((invalid.config as ReturnType<typeof Config>).title.get()).toBe('hello')
|
||||
expect(invalid._config).toEqual({ title: 'invalid', nested: { count: -1 } })
|
||||
expect((healthy.config as ReturnType<typeof Config>).title.get()).toBe('healthy')
|
||||
expect((invalid.effectiveConfig as ReturnType<typeof Config>).title.get()).toBe('hello')
|
||||
expect(invalid.rawConfig).toEqual({ title: 'invalid', nested: { count: -1 } })
|
||||
expect((healthy.effectiveConfig as ReturnType<typeof Config>).title.get()).toBe('healthy')
|
||||
expect(events).toHaveBeenCalledExactlyOnceWith([['title']])
|
||||
})
|
||||
|
||||
@@ -500,7 +500,7 @@ it.each([false, true])('retains raw expression edits for dependency restoration
|
||||
await fiber.entry!.update({ config: raw })
|
||||
await fiber.await()
|
||||
expect(values).toEqual(volatile ? ['same'] : ['same', 'same'])
|
||||
expect(fiber._config).toBe(raw)
|
||||
expect(fiber.rawConfig).toBe(raw)
|
||||
await provider.dispose()
|
||||
await provide('old-route', 'new-route')
|
||||
await fiber.await()
|
||||
@@ -540,10 +540,10 @@ it('compares ordinary URL transforms by value and remounts when an expression re
|
||||
const fixed = { __jsExpr: "ctx.get('volatile-transform-source').value" }
|
||||
const raw = { url: 'https://example.com/', fixed, live: 'old' }
|
||||
const fiber = await mountEntry(ctx, { Config: schema, apply: mount }, raw)
|
||||
const current = fiber.config as ReturnType<typeof schema>
|
||||
const current = fiber.effectiveConfig as ReturnType<typeof schema>
|
||||
await update(fiber, { ...raw, live: 'new' })
|
||||
expect(mount).toHaveBeenCalledOnce()
|
||||
expect(fiber.config).toBe(current)
|
||||
expect(fiber.effectiveConfig).toBe(current)
|
||||
expect(current.url.href).toBe(raw.url)
|
||||
expect(current.fixed).toBe('initial')
|
||||
expect(current.live.get()).toBe('new')
|
||||
@@ -554,7 +554,7 @@ it('compares ordinary URL transforms by value and remounts when an expression re
|
||||
expect(current.fixed).toBe('initial')
|
||||
expect(current.live.get()).toBe('new')
|
||||
expect(events).toHaveBeenCalledOnce()
|
||||
const replaced = fiber.config as ReturnType<typeof schema>
|
||||
const replaced = fiber.effectiveConfig as ReturnType<typeof schema>
|
||||
expect(replaced.fixed).toBe('changed')
|
||||
expect(replaced.live.get()).toBe('remounted')
|
||||
})
|
||||
@@ -568,13 +568,13 @@ it('remounts when an ordinary parent expression removes a live reference path',
|
||||
const mount = vi.fn(function (owner: Context) { owner.on('loader/volatile-update', events) })
|
||||
const raw = { block: { __jsExpr: "ctx.get('volatile-path-source').block" }, live: 'old' }
|
||||
const fiber = await mountEntry(ctx, { Config: schema, apply: mount }, raw)
|
||||
const current = fiber.config as ReturnType<typeof schema>
|
||||
const current = fiber.effectiveConfig as ReturnType<typeof schema>
|
||||
source.block = undefined
|
||||
await update(fiber, { ...raw, live: 'new' })
|
||||
expect(mount).toHaveBeenCalledTimes(2)
|
||||
expect(current.block.value.get()).toBe('old')
|
||||
expect(events).not.toHaveBeenCalled()
|
||||
expect((fiber.config as ReturnType<typeof schema>).block).toBeUndefined()
|
||||
expect((fiber.effectiveConfig as ReturnType<typeof schema>).block).toBeUndefined()
|
||||
})
|
||||
|
||||
it('keeps an ordinary plugin mounted when an absent object becomes its explicit default', async () => {
|
||||
@@ -582,13 +582,13 @@ it('keeps an ordinary plugin mounted when an absent object becomes its explicit
|
||||
const Config = z.object({ nested: z.object({ fixed: z.string() }).default({ fixed: 'default' }) })
|
||||
const mount = vi.fn()
|
||||
const fiber = await mountEntry(ctx, { Config, apply: mount }, {})
|
||||
const config: unknown = fiber.config
|
||||
const config: unknown = fiber.effectiveConfig
|
||||
await update(fiber, { nested: { fixed: 'default' } })
|
||||
expect(fiber.config).toBe(config)
|
||||
expect(fiber.effectiveConfig).toBe(config)
|
||||
expect(mount).toHaveBeenCalledTimes(1)
|
||||
await update(fiber, { nested: { fixed: 'changed' } })
|
||||
expect(mount).toHaveBeenCalledTimes(2)
|
||||
expect(fiber.config).toEqual({ nested: { fixed: 'changed' } })
|
||||
expect(fiber.effectiveConfig).toEqual({ nested: { fixed: 'changed' } })
|
||||
})
|
||||
|
||||
it('keeps a noSave volatile value as the pending raw config across a no-op entry update', async () => {
|
||||
@@ -596,11 +596,11 @@ it('keeps a noSave volatile value as the pending raw config across a no-op entry
|
||||
const fiber = await mountEntry(ctx, { Config, apply() {} }, { title: 'saved' })
|
||||
fiber.update({ title: 'ephemeral' }, true)
|
||||
await fiber.await()
|
||||
expect((fiber.config as ReturnType<typeof Config>).title.get()).toBe('ephemeral')
|
||||
expect((fiber.effectiveConfig as ReturnType<typeof Config>).title.get()).toBe('ephemeral')
|
||||
expect(fiber.entry!.options.config).toEqual({ title: 'saved' })
|
||||
await fiber.entry!.update({ config: { title: 'saved' } })
|
||||
expect(fiber._config).toEqual({ title: 'ephemeral' })
|
||||
expect((fiber.config as ReturnType<typeof Config>).title.get()).toBe('ephemeral')
|
||||
expect(fiber.rawConfig).toEqual({ title: 'ephemeral' })
|
||||
expect((fiber.effectiveConfig as ReturnType<typeof Config>).title.get()).toBe('ephemeral')
|
||||
})
|
||||
|
||||
it('isolates a failing notification listener and dispatches with a Context receiver', async () => {
|
||||
@@ -613,11 +613,11 @@ it('isolates a failing notification listener and dispatches with a Context recei
|
||||
const warning = vi.spyOn(ctx.logger, 'warn').mockImplementation(() => {})
|
||||
onTestFinished(() => { warning.mockRestore() })
|
||||
await update(fiber, { title: 'committed' })
|
||||
expect((fiber.config as ReturnType<typeof Config>).title.get()).toBe('committed')
|
||||
expect((fiber.effectiveConfig as ReturnType<typeof Config>).title.get()).toBe('committed')
|
||||
expect(receivers).toEqual([fiber])
|
||||
expect(warning).toHaveBeenCalledWith(expect.objectContaining({ message: 'listener boom' }))
|
||||
await update(other, { title: 'unaffected' })
|
||||
expect((other.config as ReturnType<typeof Config>).title.get()).toBe('unaffected')
|
||||
expect((other.effectiveConfig as ReturnType<typeof Config>).title.get()).toBe('unaffected')
|
||||
})
|
||||
|
||||
it('remounts instead of committing when an ordinary field parses into a fresh class instance', async () => {
|
||||
@@ -626,9 +626,9 @@ it('remounts instead of committing when an ordinary field parses into a fresh cl
|
||||
const schema = z.object({ handle: z.transform(z.string(), value => new Handle(value)), live: z.string().volatile() })
|
||||
const mount = vi.fn()
|
||||
const fiber = await mountEntry(ctx, { Config: schema, apply: mount }, { handle: 'h', live: 'old' })
|
||||
const before = fiber.config as ReturnType<typeof schema>
|
||||
const before = fiber.effectiveConfig as ReturnType<typeof schema>
|
||||
await update(fiber, { handle: 'h', live: 'new' })
|
||||
expect(mount).toHaveBeenCalledTimes(2)
|
||||
expect(before.live.get()).toBe('old')
|
||||
expect((fiber.config as ReturnType<typeof schema>).live.get()).toBe('new')
|
||||
expect((fiber.effectiveConfig as ReturnType<typeof schema>).live.get()).toBe('new')
|
||||
})
|
||||
@@ -426,9 +426,6 @@
|
||||
"packages/client/ui-attachment/tests/attachment-rail.client.spec.tsx": {
|
||||
"b42ab24a43e0f7542bf1deb8f7ac3a76698d29e098f8dc575af6c08774987c32": 1
|
||||
},
|
||||
"packages/client/ui-attachment/tests/composer-attachments.client.spec.tsx": {
|
||||
"ab22ebe23f59b5aa76d42502a0421f3953d1d170e8a4f900a0c442daf6f1397d": 1
|
||||
},
|
||||
"packages/client/ui-chat/src/client/conversation-nodes/turn-process.ts": {
|
||||
"a7d8cc8d42e37ad5cc4882241492ac572ed8163c7fb8663180e893c7178e906f": 1
|
||||
},
|
||||
@@ -750,9 +747,6 @@
|
||||
"packages/client/ui-workflow-run/tests/workflow-run.client.spec.tsx": {
|
||||
"9f5585ad6cebba08252165f2dcc63c1e16101e0306fab754afb641b417e7789d": 1
|
||||
},
|
||||
"packages/client/ui-workspace/tests/animated-rows.client.spec.tsx": {
|
||||
"a898fd26a24bbfa72e00b9b825779a95c1d1c2bd447ebf8f21e8b2d38c3fb08c": 1
|
||||
},
|
||||
"packages/client/ui-workspace/tests/apply.client.spec.ts": {
|
||||
"b467270b0c2a95d6b29939f6baf4d5c8e5031e99a1d15437a3060f35735d8d37": 1
|
||||
},
|
||||
|
||||
@@ -48,7 +48,6 @@
|
||||
"ask_user_question",
|
||||
"create_goal",
|
||||
"edit",
|
||||
"exit_plan_mode",
|
||||
"get_goal",
|
||||
"glob",
|
||||
"grep",
|
||||
@@ -110,7 +109,6 @@
|
||||
"ask_user_question",
|
||||
"create_goal",
|
||||
"edit",
|
||||
"exit_plan_mode",
|
||||
"get_goal",
|
||||
"glob",
|
||||
"grep",
|
||||
@@ -187,7 +185,6 @@
|
||||
"ask_user_question",
|
||||
"create_goal",
|
||||
"edit",
|
||||
"exit_plan_mode",
|
||||
"get_goal",
|
||||
"glob",
|
||||
"grep",
|
||||
|
||||
@@ -1001,6 +1001,21 @@
|
||||
"symbol": "ToolRestriction",
|
||||
"source": "packages/core/tools/src/contracts.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/tools.md",
|
||||
"symbol": "ToolActivationContext",
|
||||
"source": "packages/core/tools/src/contracts.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/tools.md",
|
||||
"symbol": "ToolRegistrationOptions",
|
||||
"source": "packages/core/tools/src/contracts.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/tools.md",
|
||||
"symbol": "ResolvedToolSet",
|
||||
"source": "packages/core/tools/src/contracts.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/tools.md",
|
||||
"symbol": "ToolFailure",
|
||||
|
||||
Reference in new issue
Block a user