import { describe, expect, it } from 'vitest' import { agentConfigToInputTransforms, agentEditorRefusal, flowLocalInputs, inputTransformsToAgentConfig, nonStaticBrainKeys, summarizeAgentBrain, toolInputOverrides, transformValuedBrainKeys } from './agentResourceUtils' describe('transformValuedBrainKeys', () => { // The tag is the whole test: `{type:'ai'}` carries no payload key to look for. it('flags every transform variant, whatever it carries', () => { expect( transformValuedBrainKeys({ system_prompt: { type: 'ai' }, temperature: { type: 'javascript', expr: 'flow_input.t' }, max_iterations: { type: 'static', value: 3 } }) ).toEqual(['system_prompt', 'temperature', 'max_iterations']) }) it('leaves the brain values that are legitimately objects', () => { expect( transformValuedBrainKeys({ output_schema: { type: 'object', properties: {} }, memory: { kind: 'auto', context_length: 20 }, provider: { kind: 'openai', model: 'gpt-4o' }, system_prompt: 'hi' }) ).toEqual([]) }) }) describe('agentEditorRefusal', () => { it('opens an agent', () => { expect(agentEditorRefusal('u/admin/agent', 'ai_agent')).toBeUndefined() }) // A path with no deployed row answers with its own draft, and the generic resource editor writes // no type: assuming `ai_agent` there would create that path as an agent on deploy. it('refuses an unknown type rather than assuming one', () => { expect(agentEditorRefusal('u/admin/db', undefined)).toContain('no deployed agent') }) }) describe('summarizeAgentBrain', () => { it('returns only set fields, in brain-key order, formatted', () => { const rows = summarizeAgentBrain({ provider: { kind: 'openai', model: 'gpt-4o', resource: '$res:f/x/openai' } as any, system_prompt: 'You are helpful', temperature: 0.7, streaming: true, max_iterations: 10 }) expect(rows).toEqual([ { label: 'Provider', value: 'openai · gpt-4o' }, { label: 'System message', value: 'You are helpful' }, { label: 'Stream the response', value: 'on' }, { label: 'Temperature', value: '0.7' }, { label: 'Max iterations', value: '10' } ]) }) it('skips empty/undefined fields', () => { expect(summarizeAgentBrain({ system_prompt: '', provider: undefined as any })).toEqual([]) expect(summarizeAgentBrain(undefined)).toEqual([]) }) it('summarizes structured fields compactly', () => { // memory is serialized with a `kind` tag (serde tag = "kind") const rows = summarizeAgentBrain({ memory: { kind: 'auto', context_length: 20 } as any, output_schema: { type: 'object' } as any }) expect(rows).toEqual([ { label: 'Memory', value: 'auto' }, { label: 'Output schema', value: 'configured' } ]) }) }) describe('inputTransformsToAgentConfig', () => { it('captures only static brain values and carries tools', () => { const config = inputTransformsToAgentConfig( { provider: { type: 'static', value: { kind: 'openai' } }, system_prompt: { type: 'static', value: 'hi' }, temperature: { type: 'javascript', expr: 'flow_input.t' }, // non-static → dropped max_iterations: { type: 'static', value: undefined }, // undefined → dropped user_message: { type: 'static', value: 'hello' } // not a brain key → dropped } as any, [{ id: 't1' }] as any ) expect(config).toEqual({ tools: [{ id: 't1' }], provider: { kind: 'openai' }, system_prompt: 'hi' }) }) it('defaults tools to []', () => { expect(inputTransformsToAgentConfig({}, undefined)).toEqual({ tools: [] }) }) }) describe('agentConfigToInputTransforms', () => { it('wraps brain values as static transforms and round-trips through the config', () => { const config = { provider: { kind: 'openai' }, system_prompt: 'hi', tools: [{ id: 't1' }] } const its = agentConfigToInputTransforms(config as any) expect(its).toEqual({ provider: { type: 'static', value: { kind: 'openai' } }, system_prompt: { type: 'static', value: 'hi' } }) const back = inputTransformsToAgentConfig(its, config.tools as any) expect(back).toEqual(config) }) }) describe('nonStaticBrainKeys', () => { it('lists brain keys with a non-static transform, in brain-key order', () => { expect( nonStaticBrainKeys({ provider: { type: 'static', value: {} }, temperature: { type: 'connected' }, system_prompt: { type: 'javascript', expr: 'x' } } as any) ).toEqual(['system_prompt', 'temperature']) }) it('flags a non-static provider (the save-blocking case) but not a static one', () => { // provider is required on the resource; a non-static one gets dropped and must block saving expect(nonStaticBrainKeys({ provider: { type: 'javascript', expr: 'x' } } as any)).toContain( 'provider' ) expect(nonStaticBrainKeys({ provider: { type: 'static', value: {} } } as any)).not.toContain( 'provider' ) }) }) describe('flowLocalInputs', () => { it('keeps the step’s own inputs, dropping brain transforms', () => { expect( flowLocalInputs({ provider: { type: 'static', value: {} }, user_message: { type: 'static', value: 'hi' }, user_attachments: { type: 'static', value: [] }, // The roster it narrows belongs to the agent, but which of it one flow may call does // not: saving this into the resource would impose it on every flow linking the agent. enabled_tools: { type: 'javascript', expr: 'flow_input.tools' } } as any) ).toEqual({ user_message: { type: 'static', value: 'hi' }, user_attachments: { type: 'static', value: [] }, enabled_tools: { type: 'javascript', expr: 'flow_input.tools' } }) }) it('handles undefined', () => { expect(flowLocalInputs(undefined)).toEqual({}) }) }) describe('toolInputOverrides', () => { const base = { tenant: { type: 'javascript', expr: 'flow_input.tenant' }, query: { type: 'static', value: 'x' } } as any it('returns nothing when inputs equal the resource base (opening a tool is a no-op)', () => { expect(toolInputOverrides(base, base)).toEqual({}) }) it('returns only the edited keys, so a revert to base drops back to nothing', () => { const edited = { ...base, query: { type: 'static', value: 'y' } } expect(toolInputOverrides(edited, base)).toEqual({ query: { type: 'static', value: 'y' } }) // reverting query back to the base value yields an empty override set again expect(toolInputOverrides(base, base)).toEqual({}) }) it('includes keys absent from the base', () => { expect(toolInputOverrides({ extra: { type: 'static', value: 1 } } as any, base)).toEqual({ extra: { type: 'static', value: 1 } }) }) })