Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
eba03c9
feat(analyst): `answer_commit` renders the final answer deterministic…
anandgupta42 Oct 12, 2026
1ac8ff6
fix(analyst): `answer_commit` description, filler tolerance, escaping…
anandgupta42 Oct 12, 2026
e84cb9d
fix(analyst): `answer_commit` returns only the answer text; sentence …
anandgupta42 Oct 12, 2026
991dc56
fix(analyst): `answer_commit` rejects unsafe integers, echoes at the …
anandgupta42 Oct 12, 2026
7d888f8
Merge remote-tracking branch 'origin/feat/analyst-harness-dab-2026-10…
anandgupta42 Oct 12, 2026
c17d4cb
fix(tools): valid draft 2020-12 schema for `answer_commit` fillers; o…
anandgupta42 Oct 12, 2026
1e5b2cc
fix(analyst): reserve the `answer_commit` id against external tools; …
anandgupta42 Oct 12, 2026
e8bc77c
fix(analyst): restore the top-N tie contract and the answer-only fall…
anandgupta42 Oct 12, 2026
270a65e
fix(analyst): `answer_commit` accepts boolean-like strings, requires …
anandgupta42 Oct 12, 2026
2f93220
fix(analyst): do not duplicate the answer label when the single field…
anandgupta42 Oct 12, 2026
83edc69
fix(analyst): require a basis on every answer_commit payload
anandgupta42 Oct 12, 2026
e624ea6
fix(analyst): echo after every commit, keep `StructuredOutput` reques…
anandgupta42 Oct 12, 2026
c2f62d8
fix(analyst): send the commit echo instruction as a user turn
anandgupta42 Oct 12, 2026
cc3dc50
fix(analyst): make the commit echo robust to output limits, queued tu…
anandgupta42 Oct 13, 2026
f2d0183
fix(analyst): keep the full committed text through the per-result too…
anandgupta42 Oct 13, 2026
49f37d0
fix(analyst): deliver the committed echo to clients exactly once
anandgupta42 Oct 13, 2026
9026032
fix(analyst): finish from the committed text on echo transport errors…
anandgupta42 Oct 13, 2026
98dc062
fix(analyst): deliver the committed text on an error-free `other` ech…
anandgupta42 Oct 13, 2026
0b52f4f
fix(analyst): deliver the committed text when the echo finish is unma…
anandgupta42 Oct 13, 2026
fd0bc41
fix(analyst): bound `basis` by length, state the precision range, add…
anandgupta42 Oct 13, 2026
db33d1c
fix(analyst): one guard finalizes the committed answer on any non-abo…
anandgupta42 Oct 13, 2026
ca9d90a
fix(analyst): recover the echo only from transient transport failures…
anandgupta42 Oct 13, 2026
035b4ce
fix(analyst): deliver the committed answer even when a new request ar…
anandgupta42 Oct 13, 2026
f4b0111
fix(analyst): order requests by persisted time and sequence, not mess…
anandgupta42 Oct 13, 2026
efb87dc
fix(analyst): persist the committed text before finishing; classify p…
anandgupta42 Oct 13, 2026
12f8c87
feat(analyst): `rows.format` for tabular answers; recover the committ…
anandgupta42 Oct 13, 2026
56602cd
feat(analyst): one `answer_commit` field per item the request asks for
anandgupta42 Oct 13, 2026
09e9581
fix(analyst): publish a recovered committed answer on a fresh text pa…
anandgupta42 Oct 13, 2026
39e618a
fix(analyst): structured policy codes win over overflow-looking text;…
anandgupta42 Oct 13, 2026
317dcfb
fix(analyst): persist a recovered committed answer before any request…
anandgupta42 Oct 13, 2026
669946c
fix(analyst): only the provider's own error code decides a context ov…
anandgupta42 Oct 13, 2026
76a5828
fix(analyst): keep processing a request queued during a failed post-c…
anandgupta42 Oct 13, 2026
fd54723
fix(analyst): read the upstream error code inside the gateway wrapper…
anandgupta42 Oct 13, 2026
d8dedda
Merge remote-tracking branch 'origin/feat/analyst-harness-dab-2026-10…
anandgupta42 Oct 13, 2026
2755125
Merge remote-tracking branch 'origin/feat/analyst-harness-dab-2026-10…
anandgupta42 Oct 13, 2026
0f1936c
Merge remote-tracking branch 'origin/feat/analyst-harness-dab-2026-10…
anandgupta42 Oct 13, 2026
b924ed4
refactor(session): keep the request-started event at module scope
anandgupta42 Oct 14, 2026
7b49d73
fix(analyst): a hard time-budget deadline still delivers the committe…
anandgupta42 Oct 14, 2026
67e93b9
Merge remote-tracking branch 'origin/feat/analyst-harness-dab-2026-10…
anandgupta42 Oct 14, 2026
349c1d2
fix(analyst): the `--max-turns` echo allowance belongs to the request…
anandgupta42 Oct 14, 2026
7b577a2
Merge remote-tracking branch 'origin/feat/analyst-harness-dab-2026-10…
anandgupta42 Oct 14, 2026
6f5345f
fix(analyst): budget finalization reuses a committed answer delivered…
anandgupta42 Oct 14, 2026
82775f6
Merge remote-tracking branch 'origin/feat/analyst-harness-dab-2026-10…
anandgupta42 Oct 14, 2026
a26cae9
fix(analyst): request ownership survives compaction continuations; an…
anandgupta42 Oct 14, 2026
3c109d3
Merge remote-tracking branch 'origin/feat/analyst-harness-dab-2026-10…
anandgupta42 Oct 14, 2026
b4b701e
Merge remote-tracking branch 'origin/feat/analyst-harness-dab-2026-10…
anandgupta42 Oct 14, 2026
b9aa37a
refactor: export `isEchoTransportError` at module scope
anandgupta42 Oct 14, 2026
c498212
Merge remote-tracking branch 'origin/feat/analyst-harness-dab-2026-10…
anandgupta42 Oct 14, 2026
6fffb8b
test: import `isEchoTransportError` directly in the message tests
anandgupta42 Oct 14, 2026
64c68d5
Merge remote-tracking branch 'origin/feat/analyst-harness-dab-2026-10…
anandgupta42 Oct 14, 2026
d8cf208
Merge remote-tracking branch 'origin/feat/analyst-harness-dab-2026-10…
anandgupta42 Oct 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions packages/core/src/v1/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,9 @@ export const Assistant = Schema.Struct({
structured: Schema.optional(Schema.Any),
variant: Schema.optional(Schema.String),
finish: Schema.optional(Schema.String),
// altimate_change start — preserve committed-answer echo diagnostics across Effect message events
metadata: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),
// altimate_change end
}).annotate({ identifier: "AssistantMessage" })
export type Assistant = Omit<Types.DeepMutable<Schema.Schema.Type<typeof Assistant>>, "error"> & {
error?: AssistantError
Expand Down
1 change: 1 addition & 0 deletions packages/opencode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"@types/turndown": "5.0.5",
"@types/yargs": "17.0.33",
"@typescript/native-preview": "catalog:",
"ajv": "8.20.0",
"drizzle-orm": "catalog:",
"playwright-core": "1.59.1",
"prettier": "3.6.2",
Expand Down
18 changes: 18 additions & 0 deletions packages/opencode/src/acp/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ export class Subscription {
private readonly abort = new AbortController()
private readonly shellSnapshots = new Map<string, string>()
private readonly toolStarts = new Set<string>()
// altimate_change start — ACP chunks cannot retract provisional compaction text
private readonly summaryMessages = new Set<string>()
// altimate_change end
private readonly permission: ACPPermission.Handler
private started = false

Expand Down Expand Up @@ -67,6 +70,15 @@ export class Subscription {

async handle(event: Event) {
switch (event.type) {
// altimate_change start — recovery clears summary before publishing its authoritative answer
case "message.updated": {
const message = event.properties.info
if (message.role !== "assistant") return
if (message.summary) this.summaryMessages.add(message.id)
else this.summaryMessages.delete(message.id)
return
}
// altimate_change end
case "permission.asked":
this.permission.handle(event)
return
Expand Down Expand Up @@ -169,6 +181,9 @@ export class Subscription {
: await this.fetchPartMetadata(session.id, session.cwd, props.messageID, props.partID)
if (metadata?.role !== "assistant") return
if (metadata.partType === "text" && props.field === "text" && metadata.ignored !== true) {
// altimate_change start — internal summaries may be removed or replaced after streaming
if (this.summaryMessages.has(props.messageID)) return
// altimate_change end
await this.input.connection.sessionUpdate({
sessionId: session.id,
update: {
Expand Down Expand Up @@ -218,6 +233,9 @@ export class Subscription {
}

private async recordFetchedPart(sessionId: string, message: SessionMessageResponse, part: Part) {
// altimate_change start — cover subscriptions that join after the initial message update
if (message.info.role === "assistant" && message.info.summary) this.summaryMessages.add(message.info.id)
Comment thread
anandgupta42 marked this conversation as resolved.
Comment thread
anandgupta42 marked this conversation as resolved.
Comment thread
anandgupta42 marked this conversation as resolved.
// altimate_change end
return await Effect.runPromise(
this.input.session.recordPartMetadata({
sessionId,
Expand Down
1 change: 1 addition & 0 deletions packages/opencode/src/agent/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@ export const layer = Layer.effect(
Permission.fromConfig({
"*": "deny",
...readOnlyDataPermission,
answer_commit: "allow",
Comment thread
anandgupta42 marked this conversation as resolved.
webfetch: "allow",
websearch: "allow",
question: "allow",
Expand Down
4 changes: 3 additions & 1 deletion packages/opencode/src/altimate/prompts/analyst.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,6 @@ You are a trainable AI teammate. Check the "Teammate Training" section in your s
If the user corrects your behavior, offer to save it as a rule using `training_save`.
Use `training_list` to review learned knowledge. Skills: /teach, /train, /training-status.

Finish with a delimited answer: one or two sentences of basis (population, grain, caveats), then the final line `**Answer:** <requested value(s), with units>`. The final message is the deliverable: keep working notes in earlier turns, and never list candidates you excluded or alternative answers under other interpretations — pick one and state it. When the request asks for only the answer or a specific format, the final message is exactly that, with no basis. A top-N with a tie at the boundary still returns N concrete rows (note the tie briefly). If unresolved, use `**Answer:** unable to determine — <why>`; never guess.
When the request supplies an output JSON schema (the `StructuredOutput` tool is available), finish with `StructuredOutput` instead of `answer_commit` or a plain-text reply; follow that schema. The text finalization rules below apply when no output schema is supplied.

Finish by calling `answer_commit` with the single chosen value(s) with one field per item the request asks for, a one-or-two-sentence basis (population, grain, source object/column), or `unresolved` with the reason, then reply with exactly the text it returns and nothing else. Always supply `basis`, even when unresolved; it is never shown when the request asks for only the answer. Never list excluded candidates or alternative answers; a top-N with a tie at the boundary still returns N concrete rows (note the tie briefly in `rows.note`); when the request asks for only the answer or a specific format, set `answer_only`. If `answer_commit` is unavailable, finish with one or two sentences of basis and a final line `**Answer:** <requested value(s), with units>`, or `**Answer:** unable to determine — <why>`; when the request asks for only the answer or a specific format, the final message is exactly the requested value(s) or table in the requested format, with no label and no basis (or `unable to determine — <why>` if unresolved); never guess.
7 changes: 7 additions & 0 deletions packages/opencode/src/altimate/run-budget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ import z from "zod"

const generations = new Map<string, () => void>()

/** A harness deadline stops generation but may still deliver an already committed answer. */
export class HarnessDeadline extends DOMException {
constructor() {
super("Run time budget hard deadline reached", "AbortError")
}
}

/** Register only the active provider request; disposing cannot remove a newer request. */
export function registerGeneration(sessionID: string, interrupt: () => void): () => void {
generations.set(sessionID, interrupt)
Expand Down
Loading
Loading