Bläddra i källkod

refactor: drop assembled section order echo

Tianyi Cui 2 månader sedan
förälder
incheckning
26e2fe9356

+ 1 - 1
docs/config-catalog.md

@@ -813,7 +813,7 @@ export interface Config {
 }
 ```
 
-Source: [`packages/core/system-prompt/src/index.ts:227`](../packages/core/system-prompt/src/index.ts)
+Source: [`packages/core/system-prompt/src/index.ts:223`](../packages/core/system-prompt/src/index.ts)
 
 ## `@deepseek-ai/dsh-tool-cordis`
 

+ 1 - 1
docs/cordis-catalog/services.md

@@ -257,7 +257,7 @@ variable(name: string, provider: (context: AssembleContext) => string | undefine
 async assemble(context: AssembleContext = {}): Promise<PromptAssembly>
 ```
 
-Source: [`packages/core/system-prompt/src/index.ts:342`](../../packages/core/system-prompt/src/index.ts)
+Source: [`packages/core/system-prompt/src/index.ts:338`](../../packages/core/system-prompt/src/index.ts)
 
 ## `ctx.tools` — `ToolRegistry`
 

+ 9 - 1
packages/bash/tool-bash/tests/tools.spec.ts

@@ -293,9 +293,17 @@ describe('bash tool', () => {
 
   it('contributes the exit-code habit as its prompt section (guidance the descriptions cannot carry)', async () => {
     const ctx = await setup()
+    ctx.systemPrompt.section({ name: 'test:before-bash', order: 104, text: 'before' })
+    ctx.systemPrompt.section({ name: 'test:after-bash', order: 106, text: 'after' })
     const assembly = await ctx.systemPrompt.assemble()
     const section = assembly.sections.find(s => s.name === 'tool:bash')
-    expect(section?.order).toBe(105)
+    expect(assembly.sections.map(s => s.name)).toEqual([
+      'harness:identity',
+      'deployment:persona',
+      'test:before-bash',
+      'tool:bash',
+      'test:after-bash',
+    ])
     expect(section?.text).toContain('[exit code: N]')
   })
 

+ 1 - 1
packages/cordis/tool-cordis/src/api-catalog.ts

@@ -544,7 +544,7 @@ export const TYPE_API: readonly TypeApiEntry[] = [
   },
   {
     name: 'AssembledSection',
-    declaration: 'export interface AssembledSection {\n    name: string;\n    order: number;\n    text: string;\n}',
+    declaration: 'export interface AssembledSection {\n    name: string;\n    text: string;\n}',
   },
   {
     name: 'BashExecRequest',

+ 2 - 7
packages/core/system-prompt/src/index.ts

@@ -103,10 +103,6 @@ export interface PromptSection {
 export interface AssembledSection {
   /** The contributing section's unique name. */
   name: string
-  // TODO(assembled-section-order): drop this output field; registry order has
-  // already sorted the array, and no production renderer/listener reads it.
-  /** The contributing section's order (sections arrive sorted ascending). */
-  order: number
   /** The resolved (but not yet interpolated) section text. */
   text: string
 }
@@ -619,12 +615,11 @@ export class SystemPrompt extends Service {
     }
     const assembly: PromptAssembly = {
       sections: [...sectionByName.values()]
+        .sort((a, b) => a.order - b.order)
         .map(section => ({
           name: section.name,
-          order: section.order,
           text: typeof section.text === 'function' ? section.text(context) : section.text,
-        }))
-        .sort((a, b) => a.order - b.order),
+        })),
       tools: orderTools(collected, this.toolOrder, knownNames),
       variables,
     }

+ 1 - 1
packages/core/system-prompt/tests/scoped.spec.ts

@@ -139,7 +139,7 @@ describe('scoped assemble dispatch', () => {
     scope.ctx.on('system-prompt/assemble', async (_assembly: PromptAssembly, context, next: () => Promise<PromptAssembly>) => {
       shaped.push(context.scope)
       const result = await next()
-      result.sections.push({ name: 'listener:extra', order: 999, text: 'listener text' })
+      result.sections.push({ name: 'listener:extra', text: 'listener text' })
       return result
     })
 

+ 13 - 13
packages/core/system-prompt/tests/system-prompt.spec.ts

@@ -21,9 +21,9 @@ describe('SystemPrompt', () => {
       await ctx.plugin(SystemPrompt, { persona: 'You are DeepSeek Harness SDK.' })
 
       const assembly = await ctx.systemPrompt.assemble()
-      expect(assembly.sections.map(s => [s.name, s.order])).toEqual([
-        ['harness:identity', -100],
-        ['deployment:persona', 0],
+      expect(assembly.sections.map(s => s.name)).toEqual([
+        'harness:identity',
+        'deployment:persona',
       ])
       expect(renderPrompt(assembly)).toBe(`${IDENTITY}\n\nYou are DeepSeek Harness SDK.`)
       // The names are reserved by the plugin — one owner per section.
@@ -183,7 +183,7 @@ describe('SystemPrompt', () => {
     const contexts: AssembleContext[] = []
     ctx.on('system-prompt/assemble', async (assembly: PromptAssembly, context, next) => {
       contexts.push(context)
-      assembly.sections.push({ name: 'from-a', order: 100, text: 'a' })
+      assembly.sections.push({ name: 'from-a', text: 'a' })
       return next()
     })
     // Listener B (registered later, runs after A) sees A's contribution.
@@ -235,8 +235,8 @@ describe('SystemPrompt', () => {
   it('filters out empty section text from renderPrompt', () => {
     const result = renderPrompt({
       sections: [
-        { name: 'empty', order: 0, text: '' },
-        { name: 'real', order: 1, text: 'content' },
+        { name: 'empty', text: '' },
+        { name: 'real', text: 'content' },
       ],
       tools: [],
       variables: {},
@@ -356,13 +356,13 @@ describe('SystemPrompt', () => {
     })
 
     it('names "(none)" when no variables are registered at all', () => {
-      expect(() => renderPrompt({ sections: [{ name: 's', order: 0, text: '{{x}}' }], tools: [], variables: {} }))
+      expect(() => renderPrompt({ sections: [{ name: 's', text: '{{x}}' }], tools: [], variables: {} }))
         .toThrow('unknown prompt variable "{{x}}" in section "s"; registered variables: (none)')
     })
 
     it('throws when a referenced variable has no value for this assembly', () => {
       expect(() => renderPrompt({
-        sections: [{ name: 'persona', order: 0, text: 'in {{cwd}}' }],
+        sections: [{ name: 'persona', text: 'in {{cwd}}' }],
         tools: [],
         variables: { cwd: undefined },
       })).toThrow('prompt variable "{{cwd}}" has no value for this assembly (section "persona")')
@@ -370,7 +370,7 @@ describe('SystemPrompt', () => {
 
     it('throws on a malformed complete reference, e.g. inner spaces', () => {
       expect(() => renderPrompt({
-        sections: [{ name: 's', order: 0, text: 'on {{ model }}' }],
+        sections: [{ name: 's', text: 'on {{ model }}' }],
         tools: [],
         variables: { model: 'm' },
       })).toThrow('malformed prompt variable reference "{{ model }}" in section "s"')
@@ -378,7 +378,7 @@ describe('SystemPrompt', () => {
 
     it('leaves a lone {{ verbatim only when NO }} follows anywhere after it', () => {
       const text = renderPrompt({
-        sections: [{ name: 's', order: 0, text: 'shell ${X:-{{fallback} stays' }],
+        sections: [{ name: 's', text: 'shell ${X:-{{fallback} stays' }],
         tools: [],
         variables: {},
       })
@@ -390,7 +390,7 @@ describe('SystemPrompt', () => {
       { text: 'x {{a{b}} y {{model}}', label: 'nested brace inside a would-be group' },
     ])('throws on a mangled reference with a }} still following ($label)', ({ text }) => {
       expect(() => renderPrompt({
-        sections: [{ name: 's', order: 0, text }],
+        sections: [{ name: 's', text }],
         tools: [],
         variables: { model: 'm' },
       })).toThrow('malformed prompt variable reference at')
@@ -400,7 +400,7 @@ describe('SystemPrompt', () => {
       // `in` would find Object.prototype.constructor and splice function
       // source into the prompt; Object.hasOwn must reject it instead.
       expect(() => renderPrompt({
-        sections: [{ name: 's', order: 0, text: 'on {{constructor}}' }],
+        sections: [{ name: 's', text: 'on {{constructor}}' }],
         tools: [],
         variables: { model: 'm' },
       })).toThrow('unknown prompt variable "{{constructor}}"')
@@ -416,7 +416,7 @@ describe('SystemPrompt', () => {
 
     it('never re-scans substituted values (a value containing {{sneaky}} stays literal)', () => {
       const text = renderPrompt({
-        sections: [{ name: 's', order: 0, text: 'v = {{model}}!' }],
+        sections: [{ name: 's', text: 'v = {{model}}!' }],
         tools: [],
         variables: { model: 'literal {{sneaky}} inside' },
       })