chore: import upstream snapshot with attribution
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
import { generateShortId } from '@sim/utils/id'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import {
|
||||
consumeLatestFileIntent,
|
||||
type PendingFileIntent,
|
||||
storeFileIntent,
|
||||
} from './file-intent-store'
|
||||
|
||||
// Force the in-memory store path so the test is deterministic and Redis-free.
|
||||
vi.mock('@/lib/core/config/redis', () => ({ getRedisClient: () => null }))
|
||||
|
||||
function makeIntent(overrides: Partial<PendingFileIntent>): PendingFileIntent {
|
||||
return {
|
||||
operation: 'update',
|
||||
fileId: 'file-x',
|
||||
workspaceId: 'ws-1',
|
||||
userId: 'user-1',
|
||||
chatId: 'chat-1',
|
||||
messageId: 'msg-1',
|
||||
fileRecord: { id: overrides.fileId ?? 'file-x' } as unknown as PendingFileIntent['fileRecord'],
|
||||
createdAt: Date.now(),
|
||||
...overrides,
|
||||
}
|
||||
}
|
||||
|
||||
function uniqueWorkspace(): string {
|
||||
return `ws-${generateShortId()}`
|
||||
}
|
||||
|
||||
describe('file-intent-store channel scoping', () => {
|
||||
it('consumes the intent for the requesting channel, not the latest in the message', async () => {
|
||||
const ws = uniqueWorkspace()
|
||||
const scope = { chatId: 'chat-1', messageId: 'msg-1' }
|
||||
|
||||
// Two concurrent file subagents: A declares fileA on channel F1 first, then
|
||||
// B declares fileB on channel F2 (later createdAt = the "latest" in message).
|
||||
await storeFileIntent(
|
||||
ws,
|
||||
'fileA',
|
||||
makeIntent({ workspaceId: ws, fileId: 'fileA', channelId: 'F1', createdAt: Date.now() })
|
||||
)
|
||||
await storeFileIntent(
|
||||
ws,
|
||||
'fileB',
|
||||
makeIntent({
|
||||
workspaceId: ws,
|
||||
fileId: 'fileB',
|
||||
channelId: 'F2',
|
||||
createdAt: Date.now() + 1000,
|
||||
})
|
||||
)
|
||||
|
||||
// edit_content from channel F1 must get fileA — NOT the latest (fileB).
|
||||
const a = await consumeLatestFileIntent(ws, { ...scope, channelId: 'F1' })
|
||||
expect(a?.fileId).toBe('fileA')
|
||||
|
||||
// edit_content from channel F2 gets fileB.
|
||||
const b = await consumeLatestFileIntent(ws, { ...scope, channelId: 'F2' })
|
||||
expect(b?.fileId).toBe('fileB')
|
||||
})
|
||||
|
||||
it('only consumes its own channel, leaving the sibling intent intact', async () => {
|
||||
const ws = uniqueWorkspace()
|
||||
const scope = { chatId: 'chat-1', messageId: 'msg-1' }
|
||||
await storeFileIntent(
|
||||
ws,
|
||||
'fileA',
|
||||
makeIntent({ workspaceId: ws, fileId: 'fileA', channelId: 'F1', createdAt: Date.now() })
|
||||
)
|
||||
await storeFileIntent(
|
||||
ws,
|
||||
'fileB',
|
||||
makeIntent({
|
||||
workspaceId: ws,
|
||||
fileId: 'fileB',
|
||||
channelId: 'F2',
|
||||
createdAt: Date.now() + 1000,
|
||||
})
|
||||
)
|
||||
|
||||
await consumeLatestFileIntent(ws, { ...scope, channelId: 'F1' })
|
||||
// The sibling (F2) is untouched and still consumable afterward.
|
||||
const b = await consumeLatestFileIntent(ws, { ...scope, channelId: 'F2' })
|
||||
expect(b?.fileId).toBe('fileB')
|
||||
})
|
||||
|
||||
it('falls back to latest-in-message when no channelId (legacy / main-agent)', async () => {
|
||||
const ws = uniqueWorkspace()
|
||||
const scope = { chatId: 'chat-1', messageId: 'msg-1' }
|
||||
await storeFileIntent(
|
||||
ws,
|
||||
'fileA',
|
||||
makeIntent({ workspaceId: ws, fileId: 'fileA', channelId: 'F1', createdAt: Date.now() })
|
||||
)
|
||||
await storeFileIntent(
|
||||
ws,
|
||||
'fileB',
|
||||
makeIntent({
|
||||
workspaceId: ws,
|
||||
fileId: 'fileB',
|
||||
channelId: 'F2',
|
||||
createdAt: Date.now() + 1000,
|
||||
})
|
||||
)
|
||||
const latest = await consumeLatestFileIntent(ws, scope)
|
||||
expect(latest?.fileId).toBe('fileB')
|
||||
})
|
||||
|
||||
it('returns undefined when the requesting channel has no pending intent', async () => {
|
||||
const ws = uniqueWorkspace()
|
||||
await storeFileIntent(
|
||||
ws,
|
||||
'fileA',
|
||||
makeIntent({ workspaceId: ws, fileId: 'fileA', channelId: 'F1', createdAt: Date.now() })
|
||||
)
|
||||
const none = await consumeLatestFileIntent(ws, {
|
||||
chatId: 'chat-1',
|
||||
messageId: 'msg-1',
|
||||
channelId: 'F-absent',
|
||||
})
|
||||
expect(none).toBeUndefined()
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user