Merge branch 'dev' into zen-body-limit
This commit is contained in:
commit
8cb0a48dff
4 changed files with 39 additions and 63 deletions
|
|
@ -15,39 +15,27 @@ const TOOL_OUTPUT_MAX_CHARS = 2_000
|
||||||
const SUMMARY_OUTPUT_TOKENS = 4_096
|
const SUMMARY_OUTPUT_TOKENS = 4_096
|
||||||
const SUMMARY_TEMPLATE = `Output exactly the Markdown structure shown inside <template> and keep the section order unchanged. Do not include the <template> tags in your response.
|
const SUMMARY_TEMPLATE = `Output exactly the Markdown structure shown inside <template> and keep the section order unchanged. Do not include the <template> tags in your response.
|
||||||
<template>
|
<template>
|
||||||
## Goal
|
## Objective
|
||||||
- [single-sentence task summary]
|
- [one or two brief sentences describing what the user is trying to accomplish]
|
||||||
|
|
||||||
## Constraints & Preferences
|
## Important Details
|
||||||
- [user constraints, preferences, specs, or "(none)"]
|
- [constraints/preferences, decisions and why, important facts/assumptions, exact context needed to continue, or "(none)"]
|
||||||
|
|
||||||
## Progress
|
## Work State
|
||||||
### Done
|
- Completed: [finished work, verified facts, or changes made; otherwise "(none)"]
|
||||||
- [completed work or "(none)"]
|
- Active: [current work, partial changes, or investigation state; otherwise "(none)"]
|
||||||
|
- Blocked: [blockers, failing commands, or unknowns; otherwise "(none)"]
|
||||||
|
|
||||||
### In Progress
|
## Next Move
|
||||||
- [current work or "(none)"]
|
1. [immediate concrete action, or "(none)"]
|
||||||
|
2. [next action if known, or "(none)"]
|
||||||
### Blocked
|
|
||||||
- [blockers or "(none)"]
|
|
||||||
|
|
||||||
## Key Decisions
|
|
||||||
- [decision and why, or "(none)"]
|
|
||||||
|
|
||||||
## Next Steps
|
|
||||||
- [ordered next actions or "(none)"]
|
|
||||||
|
|
||||||
## Critical Context
|
|
||||||
- [important technical facts, errors, open questions, or "(none)"]
|
|
||||||
|
|
||||||
## Relevant Files
|
|
||||||
- [file or directory path: why it matters, or "(none)"]
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
Rules:
|
Rules:
|
||||||
- Keep every section, even when empty.
|
- Keep every section, even when empty.
|
||||||
- Use terse bullets, not prose paragraphs.
|
- Use terse bullets, not prose paragraphs.
|
||||||
- Preserve exact file paths, commands, error strings, and identifiers when known.
|
- Preserve exact file paths, symbols, commands, error strings, URLs, and identifiers when known.
|
||||||
|
- Put relevant files and symbols inside the section where they matter; do not add extra sections.
|
||||||
- Do not mention the summary process or that context was compacted.`
|
- Do not mention the summary process or that context was compacted.`
|
||||||
|
|
||||||
type Entry = {
|
type Entry = {
|
||||||
|
|
|
||||||
|
|
@ -1097,7 +1097,7 @@ describe("SessionRunnerLLM", () => {
|
||||||
currentModel = compactModel
|
currentModel = compactModel
|
||||||
requests.length = 0
|
requests.length = 0
|
||||||
responses = [
|
responses = [
|
||||||
fragmentFixture("text", "text-summary", ["## Goal\n- Preserve the task"]).completeEvents,
|
fragmentFixture("text", "text-summary", ["## Objective\n- Preserve the task"]).completeEvents,
|
||||||
fragmentFixture("text", "text-final", ["Continued"]).completeEvents,
|
fragmentFixture("text", "text-final", ["Continued"]).completeEvents,
|
||||||
]
|
]
|
||||||
yield* session.prompt({
|
yield* session.prompt({
|
||||||
|
|
@ -1108,22 +1108,22 @@ describe("SessionRunnerLLM", () => {
|
||||||
yield* session.resume(sessionID)
|
yield* session.resume(sessionID)
|
||||||
|
|
||||||
expect(requests).toHaveLength(2)
|
expect(requests).toHaveLength(2)
|
||||||
expect(userTexts(requests[0])[0]).toContain("## Goal")
|
expect(userTexts(requests[0])[0]).toContain("## Objective")
|
||||||
expect(userTexts(requests[1])).toHaveLength(1)
|
expect(userTexts(requests[1])).toHaveLength(1)
|
||||||
expect(userTexts(requests[1])[0]).toContain("<summary>\n## Goal\n- Preserve the task\n</summary>")
|
expect(userTexts(requests[1])[0]).toContain("<summary>\n## Objective\n- Preserve the task\n</summary>")
|
||||||
expect(userTexts(requests[1])[0]).toContain(`[User]: ${"Recent exact request ".repeat(180)}`)
|
expect(userTexts(requests[1])[0]).toContain(`[User]: ${"Recent exact request ".repeat(180)}`)
|
||||||
|
|
||||||
const context = yield* (yield* SessionStore.Service).context(sessionID)
|
const context = yield* (yield* SessionStore.Service).context(sessionID)
|
||||||
expect(context.map((message) => message.type)).toEqual(["compaction", "assistant"])
|
expect(context.map((message) => message.type)).toEqual(["compaction", "assistant"])
|
||||||
expect(context[0]).toMatchObject({
|
expect(context[0]).toMatchObject({
|
||||||
type: "compaction",
|
type: "compaction",
|
||||||
summary: "## Goal\n- Preserve the task",
|
summary: "## Objective\n- Preserve the task",
|
||||||
})
|
})
|
||||||
|
|
||||||
requests.length = 0
|
requests.length = 0
|
||||||
executions.length = 0
|
executions.length = 0
|
||||||
responses = [
|
responses = [
|
||||||
fragmentFixture("text", "text-summary-2", ["## Goal\n- Preserve the updated task"]).completeEvents,
|
fragmentFixture("text", "text-summary-2", ["## Objective\n- Preserve the updated task"]).completeEvents,
|
||||||
fragmentFixture("text", "text-final-2", ["Continued again"]).completeEvents,
|
fragmentFixture("text", "text-final-2", ["Continued again"]).completeEvents,
|
||||||
]
|
]
|
||||||
yield* session.prompt({
|
yield* session.prompt({
|
||||||
|
|
@ -1135,12 +1135,12 @@ describe("SessionRunnerLLM", () => {
|
||||||
|
|
||||||
expect(requests).toHaveLength(2)
|
expect(requests).toHaveLength(2)
|
||||||
expect(userTexts(requests[0])[0]).toContain(
|
expect(userTexts(requests[0])[0]).toContain(
|
||||||
"<previous-summary>\n## Goal\n- Preserve the task\n</previous-summary>",
|
"<previous-summary>\n## Objective\n- Preserve the task\n</previous-summary>",
|
||||||
)
|
)
|
||||||
expect(userTexts(requests[0])[0]).toContain("Recent exact request")
|
expect(userTexts(requests[0])[0]).toContain("Recent exact request")
|
||||||
expect((yield* (yield* SessionStore.Service).context(sessionID))[0]).toMatchObject({
|
expect((yield* (yield* SessionStore.Service).context(sessionID))[0]).toMatchObject({
|
||||||
type: "compaction",
|
type: "compaction",
|
||||||
summary: "## Goal\n- Preserve the updated task",
|
summary: "## Objective\n- Preserve the updated task",
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
@ -1153,17 +1153,17 @@ describe("SessionRunnerLLM", () => {
|
||||||
LLMEvent.stepStart({ index: 0 }),
|
LLMEvent.stepStart({ index: 0 }),
|
||||||
LLMEvent.providerError({ message: "prompt too long", classification: "context-overflow" }),
|
LLMEvent.providerError({ message: "prompt too long", classification: "context-overflow" }),
|
||||||
],
|
],
|
||||||
fragmentFixture("text", "text-summary", ["## Goal\n- Recover overflow"]).completeEvents,
|
fragmentFixture("text", "text-summary", ["## Objective\n- Recover overflow"]).completeEvents,
|
||||||
fragmentFixture("text", "text-final", ["Recovered"]).completeEvents,
|
fragmentFixture("text", "text-final", ["Recovered"]).completeEvents,
|
||||||
]
|
]
|
||||||
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Continue" }), resume: false })
|
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Continue" }), resume: false })
|
||||||
yield* session.resume(sessionID)
|
yield* session.resume(sessionID)
|
||||||
|
|
||||||
expect(requests).toHaveLength(3)
|
expect(requests).toHaveLength(3)
|
||||||
expect(userTexts(requests[1])[0]).toContain("## Goal")
|
expect(userTexts(requests[1])[0]).toContain("## Objective")
|
||||||
expect(userTexts(requests[2])[0]).toContain("<summary>\n## Goal\n- Recover overflow\n</summary>")
|
expect(userTexts(requests[2])[0]).toContain("<summary>\n## Objective\n- Recover overflow\n</summary>")
|
||||||
expect(yield* session.context(sessionID)).toMatchObject([
|
expect(yield* session.context(sessionID)).toMatchObject([
|
||||||
{ type: "compaction", summary: "## Goal\n- Recover overflow" },
|
{ type: "compaction", summary: "## Objective\n- Recover overflow" },
|
||||||
{ type: "assistant", finish: "stop" },
|
{ type: "assistant", finish: "stop" },
|
||||||
])
|
])
|
||||||
yield* replaySessionProjection(sessionID)
|
yield* replaySessionProjection(sessionID)
|
||||||
|
|
@ -1183,7 +1183,7 @@ describe("SessionRunnerLLM", () => {
|
||||||
]
|
]
|
||||||
responses = [
|
responses = [
|
||||||
overflow(),
|
overflow(),
|
||||||
fragmentFixture("text", "text-summary", ["## Goal\n- Recover once"]).completeEvents,
|
fragmentFixture("text", "text-summary", ["## Objective\n- Recover once"]).completeEvents,
|
||||||
overflow(),
|
overflow(),
|
||||||
]
|
]
|
||||||
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Continue" }), resume: false })
|
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Continue" }), resume: false })
|
||||||
|
|
@ -1211,7 +1211,7 @@ describe("SessionRunnerLLM", () => {
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
responses = [
|
responses = [
|
||||||
fragmentFixture("text", "text-summary", ["## Goal\n- Recover raw overflow"]).completeEvents,
|
fragmentFixture("text", "text-summary", ["## Objective\n- Recover raw overflow"]).completeEvents,
|
||||||
fragmentFixture("text", "text-final", ["Recovered"]).completeEvents,
|
fragmentFixture("text", "text-final", ["Recovered"]).completeEvents,
|
||||||
]
|
]
|
||||||
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Continue" }), resume: false })
|
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Continue" }), resume: false })
|
||||||
|
|
@ -1219,7 +1219,7 @@ describe("SessionRunnerLLM", () => {
|
||||||
|
|
||||||
expect(requests).toHaveLength(3)
|
expect(requests).toHaveLength(3)
|
||||||
expect(yield* session.context(sessionID)).toMatchObject([
|
expect(yield* session.context(sessionID)).toMatchObject([
|
||||||
{ type: "compaction", summary: "## Goal\n- Recover raw overflow" },
|
{ type: "compaction", summary: "## Objective\n- Recover raw overflow" },
|
||||||
{ type: "assistant", finish: "stop" },
|
{ type: "assistant", finish: "stop" },
|
||||||
])
|
])
|
||||||
}),
|
}),
|
||||||
|
|
@ -1250,7 +1250,7 @@ describe("SessionRunnerLLM", () => {
|
||||||
const session = yield* setupOverflowRecovery
|
const session = yield* setupOverflowRecovery
|
||||||
responses = [
|
responses = [
|
||||||
[LLMEvent.providerError({ message: "prompt too long", classification: "context-overflow" })],
|
[LLMEvent.providerError({ message: "prompt too long", classification: "context-overflow" })],
|
||||||
fragmentFixture("text", "text-summary", ["## Goal\n- Interrupted"]).completeEvents,
|
fragmentFixture("text", "text-summary", ["## Objective\n- Interrupted"]).completeEvents,
|
||||||
]
|
]
|
||||||
const firstGate = yield* Deferred.make<void>()
|
const firstGate = yield* Deferred.make<void>()
|
||||||
const summaryGate = yield* Deferred.make<void>()
|
const summaryGate = yield* Deferred.make<void>()
|
||||||
|
|
|
||||||
|
|
@ -1556,33 +1556,21 @@ const scenarios: Scenario[] = [
|
||||||
const session = yield* ctx.session({ title: "Summarize session" })
|
const session = yield* ctx.session({ title: "Summarize session" })
|
||||||
yield* ctx.message(session.id, { text: "summarize this work" })
|
yield* ctx.message(session.id, { text: "summarize this work" })
|
||||||
const summary = [
|
const summary = [
|
||||||
"## Goal",
|
"## Objective",
|
||||||
"- Exercise session summarize.",
|
"- Exercise session summarize.",
|
||||||
"",
|
"",
|
||||||
"## Constraints & Preferences",
|
"## Important Details",
|
||||||
"- Use fake LLM.",
|
"- Use fake LLM.",
|
||||||
"",
|
|
||||||
"## Progress",
|
|
||||||
"### Done",
|
|
||||||
"- Summary generated.",
|
|
||||||
"",
|
|
||||||
"### In Progress",
|
|
||||||
"- (none)",
|
|
||||||
"",
|
|
||||||
"### Blocked",
|
|
||||||
"- (none)",
|
|
||||||
"",
|
|
||||||
"## Key Decisions",
|
|
||||||
"- Keep route local.",
|
"- Keep route local.",
|
||||||
|
"- Test fixture: test/server/httpapi-exercise/index.ts.",
|
||||||
"",
|
"",
|
||||||
"## Next Steps",
|
"## Work State",
|
||||||
"- (none)",
|
"- Completed: Summary generated.",
|
||||||
|
"- Active: (none)",
|
||||||
|
"- Blocked: (none)",
|
||||||
"",
|
"",
|
||||||
"## Critical Context",
|
"## Next Move",
|
||||||
"- Test fixture.",
|
"1. (none)",
|
||||||
"",
|
|
||||||
"## Relevant Files",
|
|
||||||
"- test/server/httpapi-exercise/index.ts: scenario",
|
|
||||||
].join("\n")
|
].join("\n")
|
||||||
yield* ctx.llmText(summary)
|
yield* ctx.llmText(summary)
|
||||||
yield* ctx.llmText(summary)
|
yield* ctx.llmText(summary)
|
||||||
|
|
|
||||||
|
|
@ -1430,8 +1430,8 @@ describe("session.compaction.process", () => {
|
||||||
expect(captured).toContain("<previous-summary>")
|
expect(captured).toContain("<previous-summary>")
|
||||||
expect(captured).toContain("summary one")
|
expect(captured).toContain("summary one")
|
||||||
expect(captured.match(/summary one/g)?.length).toBe(1)
|
expect(captured.match(/summary one/g)?.length).toBe(1)
|
||||||
expect(captured).toContain("## Constraints & Preferences")
|
expect(captured).toContain("## Important Details")
|
||||||
expect(captured).toContain("## Progress")
|
expect(captured).toContain("## Work State")
|
||||||
}).pipe(withCompaction({ llm: stub.llmLayer }))
|
}).pipe(withCompaction({ llm: stub.llmLayer }))
|
||||||
},
|
},
|
||||||
{ git: true },
|
{ git: true },
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue