Import AITURK IDE 1.0.0-beta.1 from Hermes 63279301; preserve MIT license

This commit is contained in:
yilsem committed 2026-09-05 13:26:46 +03:00
commit 03634b1ca3
11340 files changed
+3442369

No files matched your search

@@ -0,0 +1,231 @@
/**
* `applyAdvancedConfig` — the advanced editor's save, which persists ONLY the
* dirty sections and reports per-section outcomes back to the dialog.
*
* Two contracts live here:
*
* - **Inherit clears the pin.** A dirty model section with both fields empty
* means "inherit the gateway default", which is a `config unset model`, not
* a configure payload. Its success/failure has to merge into the same
* `applied` map as the RPC sections, or the dialog toasts a contradictory
* "saved" over a failed clear.
* - **#95293 on this surface.** The gateway now guards data-policy /
* expensive models for `profiles.configure` too, answering
* `confirm_required` + `confirm_message`. Bots does NOT route through
* use-model-controls, so before the fix that response matched no branch and
* the pick silently dropped. It must go through the SAME shared handler the
* core picker uses (one applier, no forked confirm UI), must not count as a
* FAILED section while it is merely pending, and Confirm must resend ONLY
* the model section with `confirm_expensive_model: true`.
*/
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { applyAdvancedConfig, emptyAdvancedState } from './profile-config'
import type { RosterRow } from './types'
type AdvancedConfigState = ReturnType<typeof emptyAdvancedState>
/** The payload the shared core confirm flow receives — the same object the
* model picker hands it. */
interface ModelSwitchConfirmArgs {
confirmMessage: string
finish: () => void
requestConfirmed: () => Promise<{ applied?: Record<string, boolean> } | undefined>
}
const { confirmMock, hostMock, invalidateMock } = vi.hoisted(() => ({
confirmMock: vi.fn((_args: ModelSwitchConfirmArgs) => 'notification-1'),
hostMock: {
getGateway: () => 'ambient-gateway',
request: vi.fn(),
requestProfile: vi.fn(),
state: { connectionId: { get: () => 'local' }, gateway: { get: () => 'open' }, profile: { get: () => 'default' } }
},
invalidateMock: vi.fn()
}))
vi.mock('@hermes/plugin-sdk', async () => {
const { atom } = await import('nanostores')
return {
atom,
Button: () => null,
Checkbox: () => null,
GlyphSpinner: () => null,
host: hostMock,
Input: () => null,
McpTab: undefined,
queryClient: { invalidateQueries: invalidateMock },
ScrollArea: () => null,
Select: () => null,
SelectContent: () => null,
SelectItem: () => null,
SelectTrigger: () => null,
SelectValue: () => null,
SkillsView: undefined,
surfaceModelSwitchConfirm: confirmMock,
Textarea: () => null,
ToolsetConfigPanel: undefined,
useQuery: vi.fn(() => ({ data: undefined, error: null, isLoading: false })),
useValue: vi.fn()
}
})
vi.mock('./shared', () => ({ getPluginCtx: () => null, ID: 'hermes-bots' }))
// The SOUL protocol append has its own suite; here it must not rewrite the
// text under the assertions.
vi.mock('./soul', () => ({ ensureMessagingProtocol: (soul: string) => soul }))
const bot = { name: 'zeta' } as RosterRow
const dirtyModel = (patch: Partial<AdvancedConfigState> = {}): AdvancedConfigState => ({
...emptyAdvancedState(),
dirtyModel: true,
loaded: true,
model: 'muse-spark-1.2-contributor',
provider: 'opencode-go',
...patch
})
/** Every RPC in order, with params frozen at call time. */
const routed: Array<{ method: string; params: Record<string, unknown> }> = []
function respondWith(handler: (method: string, params: Record<string, unknown>) => unknown) {
hostMock.request.mockImplementation(async (method: string, params: Record<string, unknown>) => {
routed.push({ method, params: structuredClone(params ?? {}) })
return handler(method, params)
})
}
beforeEach(() => {
vi.clearAllMocks()
routed.length = 0
respondWith(() => ({}))
})
describe('selecting Inherit', () => {
it('clears the profile model assignment through the CLI', async () => {
respondWith(() => ({ blocked: false, code: 0, output: 'Unset model' }))
const result = await applyAdvancedConfig(bot, dirtyModel({ model: '', provider: '' }))
expect(routed).toEqual([
{ method: 'cli.exec', params: { argv: ['--profile', 'zeta', 'config', 'unset', 'model'] } }
])
expect(result).toEqual({ applied: { model: true }, ok: true })
})
it('merges the clear with the other dirty sections’ outcomes', async () => {
respondWith(method => (method === 'cli.exec' ? { blocked: false, code: 0 } : { applied: { soul: true } }))
const result = await applyAdvancedConfig(
bot,
dirtyModel({ dirtySoul: true, model: '', provider: '', soul: '# Ops' })
)
expect(routed.map(call => call.method)).toEqual(['cli.exec', 'profiles.configure'])
expect(routed[1].params).toEqual({ name: 'zeta', soul: '# Ops' })
expect(result).toMatchObject({ applied: { model: true, soul: true }, ok: true })
})
it('reports a rejected clear as a failed section', async () => {
respondWith(() => ({ blocked: false, code: 1, output: 'Config key not set' }))
await expect(applyAdvancedConfig(bot, dirtyModel({ model: '', provider: '' }))).resolves.toEqual({
applied: { model: false },
ok: false
})
})
it('treats a thrown clear the same as a rejected one', async () => {
hostMock.request.mockRejectedValue(new Error('gateway down'))
await expect(applyAdvancedConfig(bot, dirtyModel({ model: '', provider: '' }))).resolves.toEqual({
applied: { model: false },
ok: false
})
})
it('refuses a half-filled model section outright', async () => {
// A provider with no model (or the reverse) is neither a pin nor a clear.
await expect(applyAdvancedConfig(bot, dirtyModel({ model: '' }))).resolves.toEqual({
applied: { model: false },
ok: false
})
expect(routed).toHaveLength(0)
})
})
describe('a guarded model switch (#95293)', () => {
it('surfaces the SHARED confirm flow instead of silently dropping the pick', async () => {
respondWith(() => ({
applied: {},
confirm_message: 'CONTRIBUTOR TIER: this model may train on your data.',
confirm_required: true,
ok: true
}))
const result = await applyAdvancedConfig(bot, dirtyModel())
expect(routed).toHaveLength(1)
expect(routed[0].method).toBe('profiles.configure')
expect(routed[0].params.model).toBe('muse-spark-1.2-contributor')
expect(routed[0].params.confirm_expensive_model).toBeFalsy()
expect(confirmMock).toHaveBeenCalledTimes(1)
expect(confirmMock.mock.calls[0][0].confirmMessage).toMatch(/CONTRIBUTOR TIER/)
// Pending confirmation is NOT a failed section — the editor must not toast
// "Some sections failed: model" while the confirm toast is still up.
expect(result.applied?.model).not.toBe(false)
expect(result.ok).toBe(true)
})
it('resends ONLY the model section on Confirm', async () => {
let calls = 0
respondWith(() => {
calls += 1
return calls === 1
? { applied: {}, confirm_message: 'guarded', confirm_required: true, ok: true }
: { applied: { model: true }, ok: true }
})
await applyAdvancedConfig(bot, dirtyModel({ dirtySoul: true, soul: '# Zeta' }))
const confirmed = await confirmMock.mock.calls[0][0].requestConfirmed()
expect(routed).toHaveLength(2)
expect(routed[1].method).toBe('profiles.configure')
expect(Object.keys(routed[1].params).sort()).toEqual(['confirm_expensive_model', 'model', 'name', 'provider'])
expect(routed[1].params).toMatchObject({
confirm_expensive_model: true,
model: 'muse-spark-1.2-contributor',
name: 'zeta',
provider: 'opencode-go'
})
expect(confirmed?.applied?.model).toBe(true)
})
it('refreshes the roster once the confirmed switch lands', async () => {
respondWith(() => ({ applied: {}, confirm_message: 'guarded', confirm_required: true, ok: true }))
await applyAdvancedConfig(bot, dirtyModel())
confirmMock.mock.calls[0][0].finish()
expect(invalidateMock).toHaveBeenCalled()
})
it('leaves an unguarded save single-shot', async () => {
respondWith(() => ({ applied: { model: true }, ok: true }))
const result = await applyAdvancedConfig(bot, dirtyModel())
expect(routed).toHaveLength(1)
expect(confirmMock).not.toHaveBeenCalled()
expect(result).toMatchObject({ applied: { model: true }, ok: true })
})
})