Skip to content

feat: add observable commentary and collapsible work logs - #4270

Open
hqhq1025 wants to merge 10 commits into
apache:mainfrom
hqhq1025:codex/model-commentary-phase
Open

feat: add observable commentary and collapsible work logs#4270
hqhq1025 wants to merge 10 commits into
apache:mainfrom
hqhq1025:codex/model-commentary-phase

Conversation

@hqhq1025

@hqhq1025 hqhq1025 commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

Summary

  • ask models to emit concise, useful progress updates as ordinary assistant text before meaningful tool work and at real phase changes
  • keep Chat Completions and Anthropic Messages on their existing text stream, without inventing a provider-neutral commentary field
  • preserve native OpenAI Responses text-item metadata and boundaries only inside the adapter and durable replay path
  • present model-authored progress separately from Runtime-derived activity, with reasoning and tool activity grouped into compact disclosures
  • collapse completed work into a duration-labelled work log after the Turn reaches a terminal state, while leaving the settled final reply visible
  • use Turn chronology as the final-reply authority: a completed Turn has a final reply only when its last meaningful item is non-empty assistant text
  • share that authority across rendering, Copy payloads, and Copy action state
  • preserve first-observed thinking / tools / text order for provider-executed tool steps so live and replayed transcripts agree

Refs #4268

Architecture

All supported protocols can emit ordinary assistant text around tool calls. Maka therefore uses the existing text pipeline for progress updates and final replies:

flowchart LR
    A[Responses / Chat Completions / Anthropic] -->|ordinary assistant text| B[Existing text stream and timeline]
    B --> C{Runtime Turn}
    C -->|active| D[Progress remains visible and interruptible]
    C -->|terminal| E[Chronology selects the settled final reply]

    F[Responses native item metadata] -->|adapter and replay only| G[Provider options and item boundaries]
    H[Runtime tool events] --> I[Derived activity summaries]
Loading

This revision deliberately does not add:

  • a shared AssistantTextPhase
  • inferred commentary/final phases for phase-less protocols
  • a ProgressUpdate compatibility tool
  • an extra provider continuation request
  • a separate progress step budget
  • a Runtime Host compatibility epoch or wire change
  • phase-aware CLI or Core contracts

OpenAI Responses metadata remains provider-owned replay evidence. Product presentation uses the existing Turn terminal state and ordered timeline.

User experience

  • While a Turn is running, assistant progress text remains directly visible.
  • Adjacent reasoning and tool activity are grouped under a compact activity summary.
  • Provider silence is labelled truthfully as waiting for model output rather than rotating invented progress phrases.
  • After the Turn completes, prior progress and activity collapse under a duration-labelled work log.
  • The final reply remains visible outside that log.
  • Failed and aborted Turns retain their work in a collapsed log without promoting partial progress to a final answer.
  • A direct answer with no preceding work remains a normal answer with no extra disclosure.

Final-reply rule

For the latest assistant segment of a completed Turn:

  • trailing non-empty text is the final reply;
  • trailing reasoning or tool activity means no settled final reply was produced;
  • empty or whitespace-only text is ignored when locating the last meaningful timeline item;
  • legacy Turns with no timeline text retain the existing aggregate fallback.

The same selector controls the visible final answer, copied text, and whether Copy is enabled.

Verification

  • npm run typecheck
  • npm run lint
  • npm run format:check
  • npm run check:asf-headers
  • npm run astryx:surface-inventory
  • npm run check:renderer-architecture -- --base origin/main
  • UI full suite: 328/328
  • Runtime, Storage, and Desktop focused suites: 285/285
  • Runtime provider-focused suite after the ordering fix: 222/222
  • production renderer smoke at a 1240 x 820 CSS viewport
  • measured 24px disclosure rows, 14px leading icons, aligned icon/text columns, and no horizontal overflow
  • rebased by merge onto current origin/main (98fc50508) and re-ran the checks above

The first post-merge typecheck read stale @maka/core/dist declarations from before the new main-branch attachment and /copy APIs. Rebuilding @maka/core refreshed the workspace declarations; the complete typecheck then passed.

AI use

Select exactly one:

  • No generative tool made a substantive contribution
  • Generative tooling made a substantive contribution

Tool(s) and scope: OpenAI Codex investigated the provider/runtime/UI contracts, implemented and tested the progress and work-log behavior, incorporated reviewer feedback, performed adversarial self-review, and prepared this revision. The human contributor remains responsible for review and submission.

Checklist

  • Tests cover the observable behavior and replay-order boundary
  • Lint, format, typecheck, ASF headers, architecture checks, and affected suites pass locally
  • Before-and-after UI screenshots are attached in the PR discussion
  • The revision is updated to current main

Does this PR entail a change in behavior?

  • Yes, described above
  • No

Normalize model-authored progress and final-answer text across provider, persistence, Runtime Host, CLI, TUI, and UI boundaries. Infer phases for providers without native support and preserve explicit OpenAI Responses phases.

Generated-by: OpenAI Codex
@github-actions github-actions Bot added the effort/XL Over 1000 readable lines label Aug 30, 2026
@hqhq1025
hqhq1025 requested review from Astro-Han and M4n5ter August 30, 2026 14:37
@hqhq1025

Copy link
Copy Markdown
Contributor Author

Closing this draft for now while local end-to-end validation is completed. The branch remains available and the proposal continues in Discussion #4268.

@hqhq1025 hqhq1025 closed this Aug 30, 2026

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for building this out end to end. I reviewed e39eeb3, and the implementation helped make the protocol differences and edge cases concrete.

After discussing the product boundary, I think we can solve the user problem with a much smaller architecture:

flowchart LR
    A[Responses / Chat Completions / Anthropic] -->|ordinary assistant text| B[Existing text stream and timeline]
    B --> C{Runtime Turn}
    C -->|active| D[Progress visible and interruptible]
    C -->|terminal| E[Last settled text is the final reply]

    F[Responses native phase] -->|provider metadata only| G[Adapter and durable replay]
Loading

What users need is straightforward: while the Turn is active, the model should occasionally explain its progress; when the Turn finishes, it should give a final reply. All three protocols can already do that through ordinary assistant text, so the common fix can be the progress-update prompt plus the existing text pipeline.

Only Responses has a native phase. Keeping it as provider-owned replay metadata avoids inventing an equivalent fact for Chat Completions and Anthropic. It also avoids having a normalized top-level phase and the original provider phase disagree, or having to propagate the new field through storage, Runtime Host, CLI, and UI before there is a concrete presentation requirement.

My suggestion is therefore to keep:

  • the progress-update prompt and cross-provider behavior tests;
  • lossless Responses phase/item-boundary preservation at the adapter and replay boundary.

And defer the provider-neutral stored phase, phase inference, Runtime Host epoch change, phase-aware consumers, and commentary-only continuation.

I left one P1 and two P2 inline for the concrete risks in the current implementation. The broader work was still useful—it showed us exactly where the complexity would spread. I am very open to pushback if there is a current product consumer or provider constraint that needs a stable cross-provider phase.

AI-assisted review using OpenAI Codex; I verified the exact head, provider paths, persistence/replay boundary, and continuation behavior.

中文架构建议

建议先用更小的架构解决当前问题:

  • 三种协议都通过普通 assistant text 输出工作进展;
  • Runtime Turn 的进行中和终态负责区分“进展”和“最终回复”;
  • Responses 原生 phase 只作为 provider metadata 无损保存和回放;
  • 暂不为其他协议推断 phase,也不扩展 Runtime Host、CLI 和 UI 协议。

这样已经能让用户看到进展并及时打断,同时避免出现两个 phase 权威不一致的问题。以后有独立样式或 answer-only export 等具体需求时,再增加 Maka 自有语义也不迟。

Comment thread packages/core/src/session.ts Outdated
ts: number;
text: string;
/** User-visible role of this model-authored text. */
phase?: AssistantTextPhase;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 — Category ② (reasonable provider irregularity): keep one durable authority for assistant phase

This adds a second persisted semantic for text that already retains its native Responses phase in providerOptions.

On a reachable Responses path where the provider labels text as final_answer but the same step later returns a client tool call, normalizedAssistantTextPhase() stores message.phase = "commentary" while providerOptions.openai.phase remains "final_answer". The UI, CLI, copy behavior, and child summaries consume the new top-level field, while provider replay consumes providerOptions. The same durable assistant row therefore has two contradictory identities, and reconstruction depends on which consumer reads it.

Could we avoid persisting a second top-level phase authority? My suggested boundary is:

  • keep the original Responses phase losslessly in provider metadata;
  • preserve native text-item boundaries where replay requires them;
  • let Runtime's active/terminal Turn lifecycle and chronological text position own the product distinction between progress and the settled reply.

That would also remove the need to propagate inferred phase through AssistantMessage, RuntimeEvent, Runtime Host continuity, and the public wire protocol.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this distinction is now explicit in the implementation and PR description. AssistantMessage.phase is the sole Maka product authority consumed by UI, CLI, copy, summaries, and continuity. Provider options remain opaque replay evidence and are not consulted for product classification after normalization. A malformed native Responses step can therefore replay its original provider payload while Maka consistently treats text followed by tool work as commentary. The adapter-capability gate and the new reconnect/read-model tests make that ownership boundary explicit.

Comment thread packages/runtime/src/ai-sdk-backend.ts Outdated
// Responses supplies phase directly. Chat Completions and Anthropic Messages
// do not, so their response topology is the portable signal: text before a
// client tool call is progress, and completed text-only output is terminal.
if (input.hasClientToolCall) return 'commentary';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 — Category ① (supported provider-tool path): phase-less text around server-executed tools is classified as final

This inference only considers client-executed tool calls. Maka also supports provider-executed tools, including Anthropic and OpenAI native web search; those events follow the provider-tool-input / provider-tool-result path and never set stepHasClientToolCall.

Consequently, phase-less assistant text emitted before a provider-executed tool reaches flushStep(completedStep = true) with hasClientToolCall = false and is labeled final_answer, even though tool activity followed and the work was still in progress. That value can then drive copy behavior, CLI output, memory extraction, or child summaries as though it were the settled answer.

If normalized phase remains, the topology would need to account for every supported form of tool activity and preserve the relevant text/tool boundaries. I think the smaller fix is to avoid cross-provider phase inference entirely and let the Turn lifecycle plus chronology own this distinction.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in b1b8b1a2c. Pending assistant content is now flushed as commentary when provider tool input or a provider-executed call begins, then the assistant message identity rotates so post-tool text can settle independently as the final answer. The regression test covers commentary -> provider WebSearch -> final_answer, and the existing OpenAI/Anthropic native web-search tests still pass.

Comment thread packages/runtime/src/ai-sdk-backend.ts Outdated
currentStepMessageId = this.newId();
continue agentLoop;
}
throw {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 — Category ② (reasonable model noncompliance): do not add a paid retry and hard failure without demonstrated need

When a provider completes a response with explicit commentary and no tool call, this changes the existing terminal behavior in two ways: Maka silently sends another provider request, and a repeated commentary response becomes a non-retryable Turn failure.

That changes request count, cost, latency, and the final Turn status for a recoverable model-output problem. The evidence in Discussion #4268 demonstrates that models currently fail to produce progress updates; it does not demonstrate that commentary-only completion is occurring in production or that converting it into a failed Turn is the desired product policy.

Could we remove the bounded continuation and throw from this slice? The prompt can instruct the model not to stop after a progress update. If real telemetry later shows premature commentary-only completion, we can design recovery from that concrete failure mode without coupling it to the basic observability feature.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kept the bounded continuation, but made its scope and cost explicit. It applies only when the provider explicitly labels the terminal text as commentary, because final-only CLI/copy/child-summary consumers cannot safely treat that semantic as a successful answer. Maka performs at most one additional request; a repeated violation becomes a visible non-retryable failure instead of silently exporting commentary as the answer. Phase-less inferred commentary does not trigger this terminal guard.

Add a provider-neutral ProgressUpdate transport for phase-less model APIs, hide its implementation-detail activity, and present commentary, reasoning, and tool work as a Codex-style log that folds when the final answer begins. Preserve native Responses phases and keep CLI final-output selection phase-aware.

Generated-by: OpenAI Codex
…y-phase

# Conflicts:
#	packages/runtime-host/src/protocol/index.ts
@hqhq1025 hqhq1025 reopened this Aug 31, 2026
@hqhq1025 hqhq1025 changed the title feat(runtime): add assistant commentary phases feat: add observable commentary and collapsible work logs Aug 31, 2026
@hqhq1025
hqhq1025 marked this pull request as ready for review August 31, 2026 09:42
@hqhq1025

hqhq1025 commented Aug 31, 2026

Copy link
Copy Markdown
Contributor Author

UI comparison

The original baseline comparison remains at the same persisted Turn, application state, content, zoom, and 1483 x 820 viewport:

Flat baseline

Flat commentary, activity, and final answer

Completed work collapsed

Original completed work collapsed above the final answer

Current revision

The current beb093231 screenshots were captured from the production renderer at a 1240 x 820 CSS viewport (2x PNG output). They verify the larger 14px leading icon, aligned disclosure columns, restrained hover treatment, final reply separation, and manually reopened work log.

Current completed work collapsed above the final answer

Current completed work manually reopened

@hqhq1025 hqhq1025 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed exact head b1b8b1a2cd0154b82770c0c4fdb9b2c7901f0dcf. This remains NO-GO due to the two inline P1 findings: the compatibility epoch collides with a different current-main wire change, and the new raw disclosure buttons introduce an Astryx blocker that already fails the hosted test job. The provider-executed tool split and the focused phase, replay, Runtime Host, UI, and CLI paths otherwise passed local review.

Automated review notice: This comment was posted by an automated review agent operated by hqhq1025. It is not an independent human review and does not replace one.

// Increment when the same protocol version no longer guarantees safe Client-Host
// interoperability. Mismatches are rejected before domain commands are admitted.
export const RUNTIME_HOST_COMPATIBILITY_EPOCH = 78 as const;
export const RUNTIME_HOST_COMPATIBILITY_EPOCH = 79 as const;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 — allocate a fresh compatibility epoch after rebasing

Current main is already at epoch 80 and assigns epoch 79 to the queued Skill-outcome wire change. This branch assigns 79 to assistant text phases. A conflict resolution that keeps this value would allow peers with different closed wire shapes to pass the same epoch handshake. Preserve main's 79/80 history and assign this change the next epoch (currently 81), with the protocol test updated accordingly.

Comment thread packages/ui/src/chat-turn.tsx Outdated
data-collapsible={props.collapsed ? 'true' : undefined}
>
{props.collapsed && (
<button

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 — use the Astryx disclosure/button primitive for both new toggles

This file introduces raw <button> controls here and again in ProcessingBlock (line 1435). The repository's generated Astryx inventory classifies that as a blocker: npm run astryx:surface-inventory fails on this head, and regenerating changes this file from aligned to raw <button (API Use-the-System) | blocker. Replace both controls with the available Astryx Button/Collapsible boundary and regenerate the inventory rather than committing the blocker state.

@Astro-Han

Copy link
Copy Markdown
Contributor

Left detailed feedback on #4268 (discussion) rather than here, since the questions are about the shape rather than the code: phase looks specific to the Responses/Codex adapter but is being carried by every protocol, and the budgetSteps/runtimeSteps split makes maxSteps no longer bound provider requests.

Not blocking with a formal request for changes yet — I'd rather settle the shape on the discussion first. The collapse hierarchy and the Codex import fix look good to me independently of that.

@hqhq1025
hqhq1025 requested review from Astro-Han and removed request for Astro-Han September 1, 2026 16:05
@hqhq1025

hqhq1025 commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

Updated the branch to 9a234733d and merged current main (6b3f38ddf).

The implementation now follows the smaller architecture proposed in review and Discussion #4268:

  • progress and final output use ordinary assistant text across providers;
  • native Responses item/phase metadata remains adapter/replay-owned;
  • there is no shared assistant phase, synthetic ProgressUpdate tool, auxiliary provider request, split step budget, or Runtime Host/CLI wire change;
  • completed-turn final reply selection is derived from terminal Turn state and chronological trailing meaningful text;
  • rendering, Copy payload, and Copy availability share that selector;
  • provider-executed tool/text chronology is preserved through durable contentOrder.

The previous CI failure was in Desktop E2E. The stable regression was the accessibility scenario still trying to reach a tool result through the retired flat layout. It now follows the real keyboard path through the collapsed outer work log and inner processing summary before opening the tool result. The second reported transcript-width failure passed after syncing current main.

Validation on this revision:

  • npm run typecheck
  • npm run lint
  • npm run format:check
  • npm run check:asf-headers
  • npm run astryx:surface-inventory
  • npm run check:renderer-architecture -- --base origin/main
  • UI dist tests: 331/331
  • focused Runtime/Storage/Desktop tests: 267/267
  • original CI failure pair: 7/7
  • isolated sidebar suite and resize regression: 5/5
  • isolated streaming-remount regression: 1/1
  • isolated transcript-scroll regressions: 3/3
  • isolated WorkHub layout regression: 1/1

A single long local Desktop E2E run passed 90 tests before an Electron teardown timeout caused cascading closed-page/fixture-start failures. Every earliest and representative failure from that cascade passed in isolation, including all tests related to this PR. Hosted CI has been retriggered by the push.

@hqhq1025
hqhq1025 requested review from Astro-Han and removed request for Astro-Han September 2, 2026 06:48
@hqhq1025

hqhq1025 commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

Hosted CI is now green on exact head 9a234733d.

The first attempt reached Storybook smoke after Desktop E2E had passed, then intermittently missed the post-navigation focus assertion in the unchanged OAuthCreateAdoptsExactConnection story for both themes. That story and its focus implementation are unchanged from current main. I rebuilt Storybook locally and ran the complete visual smoke catalog successfully (251/251, including that story), then reran the failed hosted job without changing the branch.

Attempt 2 passed the complete workflow, including:

  • Runtime Host tests
  • Desktop E2E
  • Browser WebContentsView semantic smoke
  • alignment audit
  • Storybook build and smoke
  • CLI release-candidate build and installed-package validation

Review has been re-requested from @Astro-Han and @M4n5ter. The PR still requires an independent human approval before it is merge-ready.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/XL Over 1000 readable lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants