Browse Source

fix(session): validate surface replacement endpoints

Tianyi Cui 3 weeks ago
parent
commit
b5f1539733

+ 1 - 1
packages/session/session-format-v0-to-v1/src/validation.ts

@@ -191,7 +191,7 @@ function assertSurfaceMetadata(record: Record<string, SessionFormatJsonValue>, s
   if (replacement['op'] !== 'replace') throw new SessionFormatError(`${type} ${seq} surfaceOp must replace`)
   if (replacement['op'] !== 'replace') throw new SessionFormatError(`${type} ${seq} surfaceOp must replace`)
   const start = sessionFormatCount(replacement['start'], `${type} ${seq} surface start`)
   const start = sessionFormatCount(replacement['start'], `${type} ${seq} surface start`)
   const end = sessionFormatCount(replacement['end'], `${type} ${seq} surface end`)
   const end = sessionFormatCount(replacement['end'], `${type} ${seq} surface end`)
-  if (start > end || end >= seq) throw new SessionFormatError(`${type} ${seq} has an invalid surface replacement`)
+  if (start >= seq || end >= seq) throw new SessionFormatError(`${type} ${seq} has an invalid surface replacement`)
 }
 }
 
 
 /**
 /**

+ 18 - 0
packages/session/session-format-v0-to-v1/tests/relationships.spec.ts

@@ -112,6 +112,24 @@ describe('released v1 whole-artifact relationships', () => {
     expect(decode(rows).events).toEqual(rows)
     expect(decode(rows).events).toEqual(rows)
   })
   })
 
 
+  it('accepts a surface-position range whose sequence values descend', () => {
+    const rows = [
+      { type: 'user/message', seq: 0, time: 1, data: user('zero'), surfaceOp: 'append' },
+      { type: 'user/message', seq: 1, time: 2, data: user('one'), surfaceOp: 'append' },
+      { type: 'user/message', seq: 2, time: 3, data: user('two'), surfaceOp: 'append' },
+      {
+        type: 'user/message', seq: 3, time: 4, data: user('first replacement'),
+        sourceEventSeqs: [0, 1], surfaceOp: { op: 'replace', start: 0, end: 1 },
+      },
+      {
+        type: 'user/message', seq: 4, time: 5, data: user('second replacement'),
+        sourceEventSeqs: [3, 2], surfaceOp: { op: 'replace', start: 3, end: 2 },
+      },
+    ]
+
+    expect(decode(rows).events).toEqual(rows)
+  })
+
   it('keeps the latest request provider across later steps and turns', () => {
   it('keeps the latest request provider across later steps and turns', () => {
     const rows = [
     const rows = [
       { type: 'turn/start', seq: 0, time: 1, data: { turn: 1 } },
       { type: 'turn/start', seq: 0, time: 1, data: { turn: 1 } },

+ 1 - 1
packages/session/session-format-v0-to-v1/tests/validation.spec.ts

@@ -524,7 +524,7 @@ describe('released event and payload inventory', () => {
       { sourceEventSeqs: [], surfaceOp: 'append' },
       { sourceEventSeqs: [], surfaceOp: 'append' },
       { surfaceOp: null },
       { surfaceOp: null },
       { surfaceOp: { op: 'append', start: 0, end: 1 } },
       { surfaceOp: { op: 'append', start: 0, end: 1 } },
-      { surfaceOp: { op: 'replace', start: 2, end: 1 } },
+      { surfaceOp: { op: 'replace', start: 3, end: 1 } },
       { surfaceOp: { op: 'replace', start: 1, end: 3 } },
       { surfaceOp: { op: 'replace', start: 1, end: 3 } },
     ]
     ]
     for (const metadata of surfaceVariants) {
     for (const metadata of surfaceVariants) {