fix: stabilize session fork duplicate-id errors
This commit is contained in:
@@ -8,7 +8,7 @@ Session fork service (`ctx.sessionFork`) for creating seeded child sessions from
|
|||||||
|
|
||||||
| Method | Purpose |
|
| Method | Purpose |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `snapshot(source)` | Resolve a live `Session | SessionId`, reject non-boundary logs, and return a deep-cloned seed plus `parentSession` / `seedLength` metadata. |
|
| `snapshot(source)` | Resolve a live `Session \| SessionId`, reject non-boundary logs, and return a deep-cloned seed plus `parentSession` / `seedLength` metadata. |
|
||||||
| `fork({ source, sessionId? })` | Create a live child session from `snapshot(source)`, using the caller-supplied child id or the session store's generated id. |
|
| `fork({ source, sessionId? })` | Create a live child session from `snapshot(source)`, using the caller-supplied child id or the session store's generated id. |
|
||||||
|
|
||||||
## Boundary Rule
|
## Boundary Rule
|
||||||
|
|||||||
@@ -95,10 +95,10 @@ export class SessionForkService extends Service {
|
|||||||
* `ctx.agents.create` instead.
|
* `ctx.agents.create` instead.
|
||||||
*/
|
*/
|
||||||
fork(options: ForkSessionOptions): Session {
|
fork(options: ForkSessionOptions): Session {
|
||||||
const snapshot = this.snapshot(options.source)
|
|
||||||
if (options.sessionId !== undefined && this.ctx.sessions.get(options.sessionId) !== undefined) {
|
if (options.sessionId !== undefined && this.ctx.sessions.get(options.sessionId) !== undefined) {
|
||||||
throw new SessionForkError(`session "${options.sessionId}" already exists`, 'SESSION_ALREADY_EXISTS')
|
throw new SessionForkError(`session "${options.sessionId}" already exists`, 'SESSION_ALREADY_EXISTS')
|
||||||
}
|
}
|
||||||
|
const snapshot = this.snapshot(options.source)
|
||||||
return this.ctx.sessions.create(options.sessionId, {
|
return this.ctx.sessions.create(options.sessionId, {
|
||||||
seed: snapshot.seed,
|
seed: snapshot.seed,
|
||||||
meta: snapshot.meta,
|
meta: snapshot.meta,
|
||||||
|
|||||||
@@ -203,6 +203,16 @@ describe('SessionForkService', () => {
|
|||||||
.toThrow(new SessionForkError('session "child" already exists', 'SESSION_ALREADY_EXISTS'))
|
.toThrow(new SessionForkError('session "child" already exists', 'SESSION_ALREADY_EXISTS'))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('rejects a duplicate child session id before validating the source boundary', async () => {
|
||||||
|
const { ctx, fork } = await setup()
|
||||||
|
const source = ctx.sessions.create(SessionId('open-parent'))
|
||||||
|
source.append('turn/start', { turn: 1, trigger: { kind: 'message', source: { kind: 'user' } } })
|
||||||
|
ctx.sessions.create(SessionId('child'))
|
||||||
|
|
||||||
|
expect(() => fork.fork({ source, sessionId: SessionId('child') }))
|
||||||
|
.toThrow(new SessionForkError('session "child" already exists', 'SESSION_ALREADY_EXISTS'))
|
||||||
|
})
|
||||||
|
|
||||||
it('persists a forked child seed through the existing session write path', async () => {
|
it('persists a forked child seed through the existing session write path', async () => {
|
||||||
const root = await tempRoot()
|
const root = await tempRoot()
|
||||||
const ctx = new Context()
|
const ctx = new Context()
|
||||||
|
|||||||
Reference in New Issue
Block a user