Skip to content

feat(service): report and bound approvals on service turns - #1113

Merged
edwin-zvs merged 1 commit into
mainfrom
service-approval-visibility
Aug 1, 2026
Merged

feat(service): report and bound approvals on service turns#1113
edwin-zvs merged 1 commit into
mainfrom
service-approval-visibility

Conversation

@edwin-zvs

Copy link
Copy Markdown
Contributor

The problem

A service turn stopped at a tool approval looks exactly like a slow turn from outside — the result reports only ready: false. The caller cannot learn that progress now depends on a human it has no way to reach, so it polls a session that will not move.

Observed on a live daemon: a request that triggered a file write parked on approval and stayed running indefinitely, with nothing in the response explaining why.

The change

Report the wait, and let a service bound it.

{
  "ready": false,
  "status": "running",
  "approval": {
    "outcome": "awaiting_operator",
    "tool": "edit_file",
    "summary": "1 edit in hello.txt: create `hi`",
    "waited_seconds": 5
  }
}

With approval_timeout_secs set, an unanswered call is denied on the caller's behalf when the bound elapses; the turn resumes and the reply says the action was refused rather than continuing to look unfinished.

The bound defaults to 0 (wait forever). Denying by default would refuse work an operator would have approved. Automatic approval is never offered — the party who would benefit is the party who cannot be trusted to ask for it.

Pending state is derived from the transcript rather than tracked separately: a request is pending exactly while it is the last thing of consequence, since resolutions are never recorded and the turn appends past the request once answered. No second source of truth, and it survives daemon restart for free.

Verification

Live daemon, approval_timeout_secs = 45, request triggering a gated write:

t+5s   ready=false  awaiting_operator  edit_file  "1 edit in hello.txt: create `hi`"
t+47s  daemon log:  service approval timed out; denied  tool=edit_file waited=47
after  ready=true   status=awaiting_input
       reply: "I wasn't able to create the file because the write action was denied."
       /tmp/.../work/  → empty (the denied action did not happen)

Tests: 4 new (trailing request is pending; an answered one is not; no-approval turns; the timeout default and its override). construct-daemon --lib 489, construct-cli --bins 1314, workspace builds clean.

Spec: 0171-service-approvals-are-visible-and-bounded. Follows #1111 — rebased onto it, so this diff is additive only.

Touches crates/daemon → the relevant binary is construct.

A turn stopped at a tool approval was indistinguishable from a slow turn:
the result reported only that no reply was ready. The caller had no way to
learn that progress depended on a person it could not reach, and polled a
session that would not move until someone acted.

Report the wait instead — which tool, what it would do, how long it has
been pending — and let a service bound it. When the bound elapses the call
is denied on the caller's behalf, the turn resumes, and the reply says the
action was refused rather than continuing to look unfinished. The bound
defaults to waiting indefinitely, so an operator who has not asked for a
deadline stays the only one who can decide, and automatic approval is never
offered.

Pending state is derived from the transcript: a request is pending exactly
while it is the last thing of consequence, since resolutions are not
recorded and the turn appends past the request once answered.
@edwin-zvs
edwin-zvs merged commit f19cd1d into main Aug 1, 2026
1 check passed
@edwin-zvs
edwin-zvs deleted the service-approval-visibility branch August 1, 2026 19:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant