mirror of
https://github.com/esengine/DeepSeek-Reasonix.git
synced 2026-11-07 10:45:59 -10:00
fix(delivery): align desktop marker test with exact-cut rule; trusted task text for parallel_tasks
Second review round: - The desktop topic-title regression test built its message from a paraphrased delivery-runtime marker, which the byte-exact suffix cut correctly refuses to strip — the desktop CI job failed on it. The marker constant is now exported (agent.DeliveryRuntimeMarker) as the single source of truth; the test builds from it, and the exact-match contract stays intact. Its text is byte-identical since introduction, so the exact cut is safe for all persisted sessions. - parallel_tasks children now pass the pristine task through Options.ClassifierTaskText before workspace wrapping, matching runSubSession. Without it the wrapped text hit the IsTask length fallback and a conversational child exhausted final-answer readiness instead of answering; regression test added (fails on the previous code).
This commit is contained in:
1 parent
e3052d3ce3
commit
f3029e35ed
6 files changed
+51
-10
No files matched your search
@@ -389,7 +389,9 @@ func TestTopicTitleUserTurnsSkipHostFraming(t *testing.T) {
|
||||
path := filepath.Join(dir, "session.jsonl")
|
||||
s := agent.NewSession("sys")
|
||||
// Delivery-mode first turn: user text with the trailing runtime marker.
|
||||
s.Add(provider.Message{Role: provider.RoleUser, Content: "你是谁?\n\n<delivery-runtime>\nThis session is in delivery-first mode.\n</delivery-runtime>"})
|
||||
// Built from the exported constant — the preview strip is byte-exact, so a
|
||||
// paraphrased marker would (correctly) not be stripped.
|
||||
s.Add(provider.Message{Role: provider.RoleUser, Content: "你是谁?\n\n" + agent.DeliveryRuntimeMarker})
|
||||
s.Add(provider.Message{Role: provider.RoleAssistant, Content: "reply"})
|
||||
// Host-injected readiness nudge, persisted as role user.
|
||||
s.Add(provider.Message{Role: provider.RoleUser, Content: "Host final-answer readiness check failed. Before giving a final answer, address the missing host-observable receipts: x"})
|
||||
|
||||
@@ -50,7 +50,12 @@ const maxExecutorHandoffNudges = 1
|
||||
const memoryCompilerInjectionMax = 5
|
||||
const memoryCompilerInjectionCooldown = 30 * time.Second
|
||||
|
||||
const deliveryRuntimeMarker = `<delivery-runtime>
|
||||
// DeliveryRuntimeMarker is the delivery-mode contract block appended to user
|
||||
// turns (withTurnPreferences). Exported as the single source of truth for the
|
||||
// byte-exact suffix strip in preview derivation and for cross-package tests;
|
||||
// its text is cache-frozen — changing it breaks steer replay matching and the
|
||||
// prefix stability of every live delivery session.
|
||||
const DeliveryRuntimeMarker = `<delivery-runtime>
|
||||
This session is in delivery-first mode. Before any state-changing tool call,
|
||||
establish concrete, verifiable acceptance criteria with todo_write. After the
|
||||
change, inspect the result, run relevant verification, and sign off each step
|
||||
@@ -758,7 +763,7 @@ func (a *Agent) withTurnPreferences(input string) string {
|
||||
}
|
||||
input = WithReasoningLanguage(input, lang)
|
||||
if a.deliveryProfile && !strings.Contains(input, "<delivery-runtime>") {
|
||||
input = strings.TrimSpace(input) + "\n\n" + deliveryRuntimeMarker
|
||||
input = strings.TrimSpace(input) + "\n\n" + DeliveryRuntimeMarker
|
||||
}
|
||||
return input
|
||||
}
|
||||
@@ -856,7 +861,7 @@ func SteerText(content string) (string, bool) {
|
||||
if after, found := strings.CutPrefix(s, MidTurnSteerPrefix); found {
|
||||
// Strip only the "\n" separator, preserving the user's original text.
|
||||
after = strings.TrimPrefix(after, "\n")
|
||||
if trimmed, cut := strings.CutSuffix(after, "\n\n"+deliveryRuntimeMarker); cut {
|
||||
if trimmed, cut := strings.CutSuffix(after, "\n\n"+DeliveryRuntimeMarker); cut {
|
||||
after = trimmed
|
||||
}
|
||||
return after, true
|
||||
|
||||
@@ -226,7 +226,7 @@ func TestFinalReadinessBudgetExtendsOnlyWithProgress(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPreviewStripsDeliveryMarkerAndSyntheticTurns(t *testing.T) {
|
||||
first := "你是谁?\n\n" + deliveryRuntimeMarker
|
||||
first := "你是谁?\n\n" + DeliveryRuntimeMarker
|
||||
if got := UserPreviewText(first); got != "你是谁?" {
|
||||
t.Fatalf("UserPreviewText kept framing: %q", got)
|
||||
}
|
||||
@@ -241,9 +241,9 @@ func TestPreviewStripsDeliveryMarkerAndSyntheticTurns(t *testing.T) {
|
||||
{Role: provider.RoleSystem, Content: "sys"},
|
||||
{Role: provider.RoleUser, Content: first},
|
||||
{Role: provider.RoleAssistant, Content: "hi"},
|
||||
{Role: provider.RoleUser, Content: finalReadinessRetryMessage("missing receipts") + "\n\n" + deliveryRuntimeMarker},
|
||||
{Role: provider.RoleUser, Content: finalReadinessRetryMessage("missing receipts") + "\n\n" + DeliveryRuntimeMarker},
|
||||
{Role: provider.RoleUser, Content: MidTurnSteerPrefix + "\nslow down"},
|
||||
{Role: provider.RoleUser, Content: "帮我写一个魂斗罗游戏\n\n" + deliveryRuntimeMarker},
|
||||
{Role: provider.RoleUser, Content: "帮我写一个魂斗罗游戏\n\n" + DeliveryRuntimeMarker},
|
||||
}
|
||||
preview, turns := SessionPreviewFromMessages(msgs)
|
||||
if preview != "你是谁?" {
|
||||
|
||||
@@ -180,8 +180,13 @@ func (p *ParallelTasksTool) Execute(ctx context.Context, args json.RawMessage) (
|
||||
}
|
||||
|
||||
sess := NewSession(DefaultReadOnlyTaskSystemPrompt)
|
||||
opts := p.taskTool.subagentOptions(ctx, max, pricing, ctxWin, childDepth)
|
||||
// Same contract as runSubSession: capture the pristine task before
|
||||
// host framing is prepended so delivery intent classification judges
|
||||
// the task, not the wrapper.
|
||||
opts.ClassifierTaskText = t.Prompt
|
||||
output, runErr := RunSubAgentWithSession(ctx, prov, subReg, sess, p.taskTool.withWorkspaceContext(t.Prompt),
|
||||
p.taskTool.subagentOptions(ctx, max, pricing, ctxWin, childDepth), nested)
|
||||
opts, nested)
|
||||
|
||||
if ctx.Err() != nil && runErr == nil {
|
||||
runErr = ctx.Err()
|
||||
|
||||
@@ -123,6 +123,35 @@ func TestParallelTasksInjectsWorkspaceContextIntoChildren(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestParallelTasksDeliveryClassifiesPristinePrompt pins the trusted
|
||||
// classifier channel on the parallel_tasks path: delivery intent must be
|
||||
// judged from the child's pristine prompt, not the workspace-wrapped text.
|
||||
// The wrapper is long enough that the IsTask length fallback classifies it as
|
||||
// a task; without ClassifierTaskText a plain conversational child ("Who are
|
||||
// you?") would be required to produce work receipts it has no reason to earn
|
||||
// and would exhaust final-answer readiness instead of answering.
|
||||
func TestParallelTasksDeliveryClassifiesPristinePrompt(t *testing.T) {
|
||||
workspace := t.TempDir()
|
||||
task := NewTaskTool(promptRoutingProvider{}, nil, tool.NewRegistry(), 20, 0, 0, 0, 0, 0, 0, 0.0, "", "sys", nil, 0, "", "", nil).
|
||||
WithTranscripts(NewSubagentStore(t.TempDir()), workspace, "base-model", "base-effort").
|
||||
WithDeliveryProfile(true)
|
||||
parallel := NewParallelTasksTool(task, tool.NewRegistry())
|
||||
ctx := withCallContext(context.Background(), "parallel-call", event.Discard, nil, false)
|
||||
|
||||
out, err := parallel.Execute(ctx, json.RawMessage(`{"tasks":[{"prompt":"Who are you?"},{"prompt":"Nice to meet you"}]}`))
|
||||
if err != nil {
|
||||
t.Fatalf("Execute: %v", err)
|
||||
}
|
||||
if strings.Contains(out, "readiness") {
|
||||
t.Fatalf("delivery readiness leaked into a conversational parallel child: %q", out)
|
||||
}
|
||||
// The echo provider replays the child's full user turn; both children must
|
||||
// have answered (their prompts echo back with the trailing " ok").
|
||||
if !strings.Contains(out, "Who are you?") || !strings.Contains(out, "Nice to meet you") || strings.Count(out, " ok") < 2 {
|
||||
t.Fatalf("parallel output = %q, want both children's answers", out)
|
||||
}
|
||||
}
|
||||
|
||||
// TestParallelTasksInheritLanguagePreferencesFromContext pins parallel children
|
||||
// to the same transient language injection the task tool applies: both the
|
||||
// response- and reasoning-language blocks must reach each child's user turn.
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
var reTransientUserBlock = regexp.MustCompile(`(?s)^\s*<(?:response-language|reasoning-language|memory-update|background-jobs|active-goal|hook-context|capability-route)(?:\s+[^>]*)?>.*?</(?:response-language|reasoning-language|memory-update|background-jobs|active-goal|hook-context|capability-route)>\s*\n?`)
|
||||
|
||||
// stripTrailingDeliveryRuntime removes the exact delivery-runtime marker the
|
||||
// agent appends to user turns in delivery mode (agent.go deliveryRuntimeMarker).
|
||||
// agent appends to user turns in delivery mode (agent.go DeliveryRuntimeMarker).
|
||||
// Unlike the prefix blocks it trails the user text, so preview/title derivation
|
||||
// needs a suffix cut — leaving it produced session titles like
|
||||
// "你是谁? <delivery-run…". The cut is byte-exact rather than a regex: a lazy
|
||||
@@ -19,7 +19,7 @@ var reTransientUserBlock = regexp.MustCompile(`(?s)^\s*<(?:response-language|rea
|
||||
// so user messages discussing it carry no host suffix at all.)
|
||||
func stripTrailingDeliveryRuntime(s string) string {
|
||||
trimmed := strings.TrimRight(s, " \t\r\n")
|
||||
if cut, ok := strings.CutSuffix(trimmed, deliveryRuntimeMarker); ok {
|
||||
if cut, ok := strings.CutSuffix(trimmed, DeliveryRuntimeMarker); ok {
|
||||
return strings.TrimRight(cut, " \t\r\n")
|
||||
}
|
||||
return s
|
||||
|
||||
Reference in new issue
Block a user