attachadapter

package
v2.9.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 10, 2026 License: Apache-2.0 Imports: 13 Imported by: 0

Documentation

Overview

Package attachadapter bridges a *agent.Agent onto the attach-mode HTTP/SSE surface (pkg/attach). It is phase 4 of the pkg/agent decomposition (docs/agent-package-split-design.md): the 22 Attach* capability methods and the WithAttach* provider options that used to live on the core Agent now live here, so the frozen agent surface stays narrow and hosts that never serve attach-mode never carry the wiring.

Usage:

a := agent.New(llm, ...)                 // core options only
ad := attachadapter.New(a,
    attachadapter.WithMemoryProvider(f), // formerly agent.WithAttachMemoryProvider
    attachadapter.WithPromptBroker(b),
)
reg.Register(ad)                          // ad satisfies attach.Registrant

The adapter satisfies attach.Registrant plus every optional attach capability interface (ToolsProvider, UsageProvider, PermsController, ...); registering it with a *attach.SessionRegistry makes the agent reachable over HTTP/SSE via attach.NewServer.

Index

Constants

This section is empty.

Variables

View Source
var ErrSubagentSpawnerUnavailable = errors.New("attachadapter: subagent spawner unavailable (no BackgroundAgentManager wired)")

ErrSubagentSpawnerUnavailable is returned by AttachSpawnSubagent when the agent wasn't constructed with agent.WithBackgroundManager. The attach handler maps this to HTTP 501 so the operator sees "subagent spawn not registered" instead of a 500.

The message string is load-bearing: pkg/attach matches it literally (it can't import this package's sentinel without knowing about the agent side), so change it only in lockstep with isSubagentSpawnerUnavailable in pkg/attach/handlers_slash.go. The prefix says "attachadapter" — the sentinel's home since the #443 split (the stale "agent:" prefix from its old home was fixed under #492 item 5).

Functions

func DescribePlanOwner added in v2.9.0

func DescribePlanOwner(p tools.PlanInfo) string

DescribePlanOwner renders a plan artifact's frontmatter attribution for the /replan message that reports a plan it declined to archive (#747). It states what the file says rather than inferring: "another agent" would be a guess, and the likeliest way to reach this branch is the same agent in an earlier session after a daemon restart.

func ReplanHandler added in v2.9.0

func ReplanHandler(gate *permissions.Gate, agentsDir string, owner func() tools.PlanOwner) func(context.Context, attach.ReplanRequest) (attach.ReplanResponse, error)

ReplanHandler builds the closure WithReplanner wants: archive the caller's own active plan artifact, clear the gate's planRecorded flag, and describe what actually happened in terms the operator can act on.

It lives here rather than in a host because there are two hosts. The single-session CLI wired this inline; the multi-session hub left /replan returning 501, which meant a recipe running plan_mode "required" under the hub could arm the plan-first gate and never revoke it from the session that owned the plan (#763). Duplicating the closure would have duplicated the parts #747 proved are load-bearing: the owner scoping and the three-way message.

gate is required — clearing the flag is half the contract, and a handler that silently skipped it would report a revocation that didn't happen; a nil one degrades to an honest error rather than panicking a tenant's request. agentsDir may be empty; the closure then reports that plan artifacts have nowhere to live instead of pretending success.

owner is a func, not a value, because both hosts learn the agent's identity after the adapter options are built (the CLI late-binds agentRef via WithPostConstruct). A nil owner, or one returning the zero PlanOwner, degrades to newest-wins — the pre-#747 behavior, which is the right fallback for a /replan that beat construction.

Types

type Adapter

type Adapter struct {
	// contains filtered or unexported fields
}

Adapter wraps a *agent.Agent with the attach-facing capability surface. Construct with New; the zero value is not useful.

The capability methods (Attach*) are safe on a nil receiver — they degrade to the same "capability not registered" responses an unwired closure produces, matching the nil-safety convention of the agent package. The plain Registrant forwards (AppName, Inject, ...) assume a real wrapped agent, same as registering a bare *agent.Agent did before the split.

func New

func New(a *agent.Agent, opts ...Option) *Adapter

New wraps a with the attach capability surface.

Contract (one rule, not two): the CAPABILITY methods (Attach*) are nil-safe — on a nil adapter or nil wrapped agent they degrade to the same "capability not registered" / zero-value responses an unwired closure produces. The plain attach.Registrant forwards are NOT nil-safe: they require a real wrapped agent and misbehave otherwise (the identity accessors — AppName, SessionID, EventLog — panic; Inject/InjectAs error; RequestWake no-ops), exactly as registering a bare *agent.Agent did before the #443 split. Passing a nil agent is therefore only useful for constructing a capability-only value in tests — never register one (attach.SessionRegistry would reject its empty identity anyway).

func (*Adapter) Agent

func (ad *Adapter) Agent() *agent.Agent

Agent returns the wrapped *agent.Agent. Hosts that thread the adapter through construction seams (session factories, TUI deps) use this to recover the agent for Run/Inject/etc without carrying both values.

func (*Adapter) AppName

func (ad *Adapter) AppName() string

AppName implements attach.Registrant.

func (*Adapter) AttachAddAllow

func (ad *Adapter) AttachAddAllow(patterns []string) error

AttachAddAllow implements attach.PermsController. Delegates to permissions.Gate.AddAllowPatterns. Returns nil if no gate was wired (no-op rather than error — operators shouldn't see an error for an absent gate). Surfaces validation errors from the gate so the operator sees malformed-pattern feedback.

func (*Adapter) AttachAddDeny

func (ad *Adapter) AttachAddDeny(patterns []string) error

AttachAddDeny implements attach.PermsController. Delegates to permissions.Gate.AddDenyPatterns.

func (*Adapter) AttachAgents

func (ad *Adapter) AttachAgents() []attach.AgentInfo

AttachAgents implements attach.AgentsProvider. Returns the live background subagents from the agent's SubagentManager, or an empty slice when no manager was wired.

func (*Adapter) AttachAskSideQuestion

func (ad *Adapter) AttachAskSideQuestion(ctx context.Context, question string) (string, error)

AttachAskSideQuestion implements attach.SideQueryProvider. Wraps agent.AskSideQuestion (the /btw side-channel that doesn't persist to the event log).

The agent's empty-answer error is translated into the wire package's equivalent so the handler can answer 200 + empty:true without pkg/attach importing pkg/agent (which imports it).

func (*Adapter) AttachCapabilities

func (ad *Adapter) AttachCapabilities() attach.CapabilityReport

AttachCapabilities implements attach.CapabilityReporter (#490). The adapter satisfies every optional capability interface unconditionally (see the conformance block below), so interface presence stopped signaling wiredness the moment the adapter became the universal registration path — every session advertised mcp/perms_stream/specialists and all five slash commands, and remote UIs rendered dead affordances backed by empty payloads or 501s. This report states what is actually wired:

  • perms_stream ⇔ a prompt broker was supplied (WithPromptBroker);
  • mcp ⇔ an MCP snapshot fn was supplied (WithMCPProvider);
  • specialists / "subagent" ⇔ the agent carries a background manager (agent.WithBackgroundManager);
  • interrupt, guardrails, "btw" ⇔ a live agent is wrapped (all three are core agent capabilities — Interrupt, the guardrail read/reset pair and AskSideQuestion need no extra wiring);
  • cost_ceiling ⇔ a per-turn or per-session spend cap is armed (#666 — a live-state read, not interface presence);
  • "compact" ⇔ agent.HasCompactor(); "done" ⇔ HasCheckpointer();
  • "replan" ⇔ a replanner fn was supplied (WithReplanner).

func (*Adapter) AttachCheckpoint

func (ad *Adapter) AttachCheckpoint(ctx context.Context, note string) (attach.CheckpointResponse, error)

AttachCheckpoint implements attach.CheckpointSlashProvider. Wraps agent.Checkpoint.

func (*Adapter) AttachCompact

func (ad *Adapter) AttachCompact(ctx context.Context, focus string) (attach.CompactResponse, error)

AttachCompact implements attach.CompactSlashProvider. Wraps agent.Compact and projects the result into the JSON wire format. Errors propagate; the attach handler turns them into 500s.

func (*Adapter) AttachContext

func (ad *Adapter) AttachContext() attach.ContextInfo

AttachContext implements attach.ContextProvider. Projects the agent's ContextStats (compaction / checkpoint / subtask shape) into the attach wire format. Same cost as ContextStats (one session.Service.Get() + O(events) scan) — operator-driven, infrequent.

func (*Adapter) AttachGuardrails added in v2.9.0

func (ad *Adapter) AttachGuardrails() attach.GuardrailInfo

AttachGuardrails implements attach.GuardrailProvider. Projects the agent's watchdog + cost-ceiling state into the wire shape. Nil-safe: a capability-only adapter reports every backstop off and nothing tripped, which is the truthful answer for an agent with no guardrails.

func (*Adapter) AttachInterrupt

func (ad *Adapter) AttachInterrupt() bool

AttachInterrupt implements attach.InterruptProvider so the attach-mode POST /sessions/<sid>/interrupt handler can dispatch cancel intents from a remote operator. Forwards to agent.Interrupt.

func (*Adapter) AttachInterruptHold added in v2.9.0

func (ad *Adapter) AttachInterruptHold(reason string) (bool, bool)

AttachInterruptHold implements attach.PauseController. Cancels the in-flight turn and parks the loop atomically.

func (*Adapter) AttachMCP

func (ad *Adapter) AttachMCP() attach.MCPInfo

AttachMCP implements attach.MCPProvider.

func (*Adapter) AttachMemory

func (ad *Adapter) AttachMemory() []attach.MemorySource

AttachMemory implements attach.MemoryProvider. Returns nil when no provider was wired — the handler emits 200 with an empty `{"sources": []}`.

func (*Adapter) AttachPause added in v2.9.0

func (ad *Adapter) AttachPause(reason string) bool

AttachPause implements attach.PauseController. Parks the loop without touching an in-flight turn.

func (*Adapter) AttachPauseState added in v2.9.0

func (ad *Adapter) AttachPauseState() attach.PauseInfo

AttachPauseState implements attach.PauseController.

func (*Adapter) AttachPerms

func (ad *Adapter) AttachPerms() attach.PermsInfo

AttachPerms implements attach.PermsProvider. Returns the gate's current Snapshot (mode + allow + deny pattern lists) projected into the attach wire format, plus the per-session approval log so the remote TUI's /permissions slash can render what was approved this session. Returns zero PermsInfo if no gate was wired via agent.WithGate.

func (*Adapter) AttachPricing

func (ad *Adapter) AttachPricing() attach.PricingInfo

AttachPricing implements attach.PricingProvider.

func (*Adapter) AttachPromptBroker

func (ad *Adapter) AttachPromptBroker() *attach.PromptBroker

AttachPromptBroker implements attach.PromptBrokerProvider.

func (*Adapter) AttachRefreshPricing

func (ad *Adapter) AttachRefreshPricing(ctx context.Context) (attach.PricingRefreshResponse, error)

AttachRefreshPricing implements attach.PricingController. Returns attach.ErrCapabilityNotRegistered when no func was wired — the handler maps that to HTTP 501.

func (*Adapter) AttachReload

func (ad *Adapter) AttachReload(ctx context.Context) attach.ReloadResponse

AttachReload implements attach.Reloader. Returns a response with Errors populated by ErrCapabilityNotRegistered when no func was wired so the handler emits the same 501 the other unwired controllers do.

func (*Adapter) AttachReplan

func (ad *Adapter) AttachReplan(ctx context.Context, req attach.ReplanRequest) (attach.ReplanResponse, error)

AttachReplan implements attach.ReplanProvider. Routes to the closure wired by WithReplanner; returns ErrCapabilityNotRegistered when no func was wired.

func (*Adapter) AttachResetGuardrail added in v2.9.0

func (ad *Adapter) AttachResetGuardrail(req attach.GuardrailResetRequest) (attach.GuardrailResetResponse, error)

AttachResetGuardrail implements attach.GuardrailResetter.

Order of operations matters and is deliberate:

  1. Decide whether the reset can work BEFORE mutating anything. A per-session cost trip whose spend already exceeds ceiling + requested budget is refused with attach.ErrGuardrailRetrip and leaves the agent untouched — no half-applied budget, and no 200 that the next turn undoes.
  2. Raise the ceiling, then clear the flag. The reverse order leaves a window where a concurrent post-turn enforcement pass re-trips against the old ceiling.

The refusal in (1) is whole-request, not per-guardrail: an under-budget guardrail=all on a session that ALSO tripped the watchdog clears neither. Clearing just the watchdog would leave the session halted anyway (the ceiling still refuses the next turn), so a partial 200 would only obscure that the operator's request didn't work. Scope to guardrail=watchdog to clear one in isolation.

Budget is applied whenever cost_ceiling is in scope, tripped or not: raising the bar before a long run hits it is the same affordance as raising it after, and refusing the pre-emptive case would just teach operators to wait for the halt.

func (*Adapter) AttachResume added in v2.9.0

func (ad *Adapter) AttachResume(req attach.ResumeRequest, caller auth.Caller) (attach.ResumeResponse, error)

AttachResume implements attach.PauseController: resolve the disposition, frame the operator's text, and hand the whole thing to agent.ResumeWith (which queues before it opens the gate, so the released turn can't start ahead of the instruction).

Mode defaults by content — steer when there's text, continue when there isn't — so the common client can send just {"steer": "..."} or an empty body.

func (*Adapter) AttachSetManualPricing

func (ad *Adapter) AttachSetManualPricing(req attach.PricingSetRequest) error

AttachSetManualPricing implements attach.PricingController.

func (*Adapter) AttachSkills

func (ad *Adapter) AttachSkills() []attach.SkillInfo

AttachSkills implements attach.SkillsProvider.

func (*Adapter) AttachSpawnSubagent

func (ad *Adapter) AttachSpawnSubagent(ctx context.Context, spec attach.SubagentSpec) (attach.SubagentSpawnResponse, error)

AttachSpawnSubagent implements attach.SubagentSpawner. Delegates to the agent's wired SubagentManager. Returns ErrSubagentSpawnerUnavailable when no manager is attached.

func (*Adapter) AttachStatus

func (ad *Adapter) AttachStatus() attach.StatusInfo

AttachStatus implements attach.StatusProvider. Returns the agent's model name plus its coarse state: "paused" when an operator has parked the loop (v1.5.0), "running" while a turn is executing (v1.12.0), "idle" otherwise. "deferred" is still unproduced — nothing on the agent exposes a scheduled wake time to read.

TurnInFlight is set from the same signal that decides "running" and is reported independently of State, because pause outranks running in a one-field State and "parked while the interrupted turn is still finishing" is exactly the state an operator needs to see (#896). Before this, a mid-turn GET /status answered "idle" and the SSE status seed answered turn_state:"idle" — on a per-incident watcher session the seed is the only thing an operator ever gets, since the session and its first turn are born together.

func (*Adapter) AttachStopAgent added in v2.9.0

func (ad *Adapter) AttachStopAgent(name string) (bool, error)

AttachStopAgent implements attach.AgentStopper, the pre-1.12.0 spelling. Kept so a client or embedder holding the older interface still resolves; the handler prefers AttachStopAgentOutcome and only falls back here for registrants that don't have it.

func (*Adapter) AttachStopAgentOutcome added in v2.9.0

func (ad *Adapter) AttachStopAgentOutcome(name string) (attach.StopAgentOutcome, error)

AttachStopAgentOutcome implements attach.AgentStopReporter. Stops one background subagent by name and reports what the attempt did: Found=false only when the manager has never registered that name (which the handler turns into a 404), Stopped=false with a terminal Status when the subagent had already finished on its own.

func (*Adapter) AttachSubagentCatalog added in v2.9.0

func (ad *Adapter) AttachSubagentCatalog() []attach.SubagentCatalogInfo

AttachSubagentCatalog implements attach.SubagentCatalogProvider (#627). Returns the CONFIGURED subagent roster — declarative templates + predefined catalog specs the manager was wired with — distinct from AttachAgents (live/spawned instances). Empty when no manager is wired (e.g. --no-background-agents): nothing is spawnable by reference, so the roster is empty; the sync subagent tools still appear in AttachTools with source="subagent".

func (*Adapter) AttachTools

func (ad *Adapter) AttachTools() []attach.ToolInfo

AttachTools implements attach.ToolsProvider. Returns the agent's full tool catalog as ToolInfo entries with source classification (builtin / subagent / mcp / skill / other), MCP server attribution, and the gate's pre-flight state per tool when a gate was wired via agent.WithGate.

The catalog comes from three places because the agent holds it in three places (#767). agent.Tools() carries built-ins and the synchronous subagent tools. MCP tools and skill tools reach the agent as TOOLSETS — agent.WithToolsets, never agent.WithTools — so they are not in agent.Tools() at all and were previously missing from this endpoint entirely, not merely misclassified. They are folded in from the snapshot providers instead of by enumerating the live toolsets, which is a deliberate trade: the MCP snapshot is materialized once at startup (mcp.Server.ToolInfos), so /tools stays a pure in-memory read rather than fanning out a tools/list round-trip per server on an operator keystroke — and it cannot disagree with what /mcp reports, since both read the same snapshot.

Unwired providers omit their section rather than erroring: an embedder with no MCP wiring has no MCP tools to report.

func (*Adapter) AttachUsage

func (ad *Adapter) AttachUsage() attach.UsageInfo

AttachUsage implements attach.UsageProvider. Returns the agent's usage tracker totals plus a per-model breakdown when more than one model has been used in this session (typical pattern: parent on a frontier model, subtasks on a cheap flash-tier model via --agentic-small-model), plus a per-turn array so operators can answer per-turn cost/cache questions without hand-scraping the eventlog (issue #222). Returns a zero UsageInfo if no usage tracker was wired (agent.WithUsageTracker).

cost_usd_uncached_reference is computed per-turn using the resolved pricing for that turn's model — sessions that mix models (parent + subtask on a flash-tier via --agentic-small-model) get accurate reference numbers instead of averaging one model's rates over the other. Rolled up into Overall / PerModel by summing per-turn contributions.

func (*Adapter) Description

func (ad *Adapter) Description() string

Description implements attach.DescriptionProvider — the /.well-known/agent-card.json handler falls back to this when no explicit AgentCardConfig.Description override is supplied.

func (*Adapter) EventLog

func (ad *Adapter) EventLog() *eventlog.Handle

EventLog implements attach.Registrant.

func (*Adapter) Inject

func (ad *Adapter) Inject(message string) error

Inject implements attach.Registrant.

func (*Adapter) InjectAs

func (ad *Adapter) InjectAs(message string, caller auth.Caller) error

InjectAs implements attach.Registrant.

func (*Adapter) InjectAsContext added in v2.9.0

func (ad *Adapter) InjectAsContext(ctx context.Context, message string, caller auth.Caller) error

InjectAsContext implements attach.ContextInjector — InjectAs plus the injecting request's context, so the turn that drains the message can link back to the span that queued it.

func (*Adapter) InjectAsContextWithID added in v2.9.0

func (ad *Adapter) InjectAsContextWithID(ctx context.Context, message string, caller auth.Caller) (string, error)

InjectAsContextWithID implements attach.IdentifyingInjector — InjectAsContext returning the prompt_id, so POST /inject can echo the handle a client keys per-turn state on (#840).

Here for the same reason QueueAsContext is: the handler's capability assertion runs against this adapter, not the agent it wraps, so an unforwarded method means every response silently omits `prompt_id` while every direct-agent test still passes.

func (*Adapter) MarkInterruptPending

func (ad *Adapter) MarkInterruptPending()

MarkInterruptPending implements attach.InterruptSelfAuditor. The /interrupt handler calls it (instead of appending the audit row out-of-band) so the audit is written from the agent's own turn loop after the interrupted turn unwinds, dodging the OCC race that mislabeled operator cancels as stale-session errors (#565). Forwards to agent.MarkInterruptPending.

func (*Adapter) QueueAsContext added in v2.9.0

func (ad *Adapter) QueueAsContext(ctx context.Context, message string, caller auth.Caller) error

QueueAsContext implements attach.DeferredInjector — InjectAsContext minus the wake, backing POST /inject with {"wake": false} (#698).

It has to be here, not only on *agent.Agent: this adapter is what hosts register with the attach registry, so the handler's capability assertion runs against the Adapter. Forwarding the waking half and not this one would answer every deferred inject with 501 while every direct-agent test still passed.

func (*Adapter) QueueAsContextWithID added in v2.9.0

func (ad *Adapter) QueueAsContextWithID(ctx context.Context, message string, caller auth.Caller) (string, error)

QueueAsContextWithID implements attach.IdentifyingDeferredInjector — the wake:false half of the same forward.

func (*Adapter) RequestWake

func (ad *Adapter) RequestWake()

RequestWake implements attach.Registrant.

func (*Adapter) SessionID

func (ad *Adapter) SessionID() string

SessionID implements attach.Registrant.

func (*Adapter) SessionTitle added in v2.9.0

func (ad *Adapter) SessionTitle() string

SessionTitle implements attach.SessionTitleProvider. Unprefixed, unlike its neighbours, because it forwards a method of the same name on the agent rather than projecting adapter-local state — and because the attach interface names the method for what a session has, not for where it is read from.

Empty until the first turn's generation lands (or an operator renames the session); GET /sessions omits the field rather than sending "".

func (*Adapter) SetAttachEmitter deprecated

func (ad *Adapter) SetAttachEmitter(f func(eventType string, payload any))

SetAttachEmitter is the pre-#506 name.

Deprecated: use SetOperatorEventEmitter.

func (*Adapter) SetOperatorEventEmitter

func (ad *Adapter) SetOperatorEventEmitter(f func(eventType string, payload any))

SetOperatorEventEmitter implements attach.OperatorEventTarget. The attach broadcaster calls this on first SSE subscriber (wiring its Emit method) and again with nil when the last subscriber disconnects. Forwards to the agent, which owns the emit machinery — the core run loop is the thing that emits status/turn/usage events.

func (*Adapter) SetSessionTitle added in v2.9.0

func (ad *Adapter) SetSessionTitle(title string)

SetSessionTitle implements attach.SessionTitleSetter — the write half of the same capability, backing POST /sessions/{sid}/title.

It has to be here, not only on *agent.Agent: this adapter is what gets registered with the attach registry, so the handler's type assertion runs against the Adapter. A read half that forwards and a write half that doesn't would answer every rename with 501 while every direct-agent test still passed.

Setting "" clears the title and re-arms automatic generation; the agent normalizes, which is why the handler reads the title back rather than echoing the request.

func (*Adapter) UserID

func (ad *Adapter) UserID() string

UserID implements attach.Registrant.

type Option

type Option func(*Adapter)

Option configures an Adapter under construction.

func WithMCPProvider

func WithMCPProvider(fn func() attach.MCPInfo) Option

WithMCPProvider wires a snapshot func for /sessions/<sid>/mcp (backs /mcp). Also the source AttachTools attributes MCP tools from, so /tools and /mcp cannot disagree about which server owns what. Formerly agent.WithAttachMCPProvider.

func WithMemoryProvider

func WithMemoryProvider(fn func() []attach.MemorySource) Option

WithMemoryProvider wires a snapshot func that returns the agent's loaded instruction sources for the remote-attach /sessions/<sid>/memory endpoint (backs the remote TUI's /memory slash). The caller usually projects an `instruction.Loaded`'s Sources list into []attach.MemorySource; nil = endpoint returns empty. Formerly agent.WithAttachMemoryProvider.

func WithPricingProvider

func WithPricingProvider(fn func() attach.PricingInfo) Option

WithPricingProvider wires a snapshot func for /sessions/<sid>/pricing (backs the remote TUI's /pricing read). Formerly agent.WithAttachPricingProvider.

func WithPricingSetter

func WithPricingSetter(fn func(req attach.PricingSetRequest) error) Option

WithPricingSetter wires a func that runs on POST /sessions/<sid>/pricing/set — writes a manual per-model rate and rebuilds the catalog. Formerly agent.WithAttachPricingSetter.

func WithPromptBroker

func WithPromptBroker(b *attach.PromptBroker) Option

WithPromptBroker wires the broker that bridges the agent's permissions.Gate prompts to remote operators over GET /sessions/<sid>/perms/stream and POST /perms/respond. The caller is also responsible for wiring this broker into the gate (typically via Gate.SetPrompter(broker)) so prompts the gate generates actually flow through it. Without this option the /perms/stream + /perms/respond routes return 501. Formerly agent.WithAttachPromptBroker.

func WithRefreshPricer

func WithRefreshPricer(fn func(ctx context.Context) (attach.PricingRefreshResponse, error)) Option

WithRefreshPricer wires a func that runs on POST /sessions/<sid>/pricing/refresh — typically calls into `pkg/pricing.Refresh` and rebuilds the catalog. Returns the outcome the operator sees. Formerly agent.WithAttachRefreshPricer.

func WithReloader

func WithReloader(fn func(ctx context.Context) attach.ReloadResponse) Option

WithReloader wires a func that runs on POST /sessions/<sid>/reload. The closure is expected to re-walk project deps (instruction sources, skills bundles, MCP config) and return per-surface success in the response so the operator sees which parts succeeded and which failed. The adapter doesn't inspect the response shape; what "reload" means is the host's concern. Without this option the operator sees 501 / capability not registered. Formerly agent.WithAttachReloader.

func WithReplanner

func WithReplanner(fn func(ctx context.Context, req attach.ReplanRequest) (attach.ReplanResponse, error)) Option

WithReplanner wires a func that runs on POST /sessions/<sid>/slash/replan and on the in-process TUI's /replan slash dispatch. The closure is expected to clear the gate's planRecorded flag and archive the latest plan artifact (typically `tools.RevokeLatestPlan(gate, agentsDir)`). Without this option the slash returns 501 / "capability not registered".

Wiring it under `plan_mode: "advisory"` or `"off"` is harmless — there is no gate flag set, so the closure archives whatever artifact exists (or reports none) and blocks nothing. The CLI wires it unconditionally for that reason; the closure's own response text is what distinguishes the modes, since telling an operator "the next mutating call will be denied" when advisory mode will deny nothing is the same unenforced-claim bug the mode exists to avoid. Formerly agent.WithAttachReplanner.

func WithSkillToolsProvider added in v2.9.0

func WithSkillToolsProvider(fn func() []attach.ToolInfo) Option

WithSkillToolsProvider wires a snapshot func naming the TOOLS the skill toolset exposes — list_skills / load_skill / the rest — as opposed to WithSkillsProvider, which names the installed skills. AttachTools folds these into /sessions/<sid>/tools with source="skill"; without it they are invisible there, because skills reach the agent as a toolset and never enter agent.Tools() (#767). Project a skills.Skills' ToolInfos(); nil = skill tools are omitted.

func WithSkillsProvider

func WithSkillsProvider(fn func() []attach.SkillInfo) Option

WithSkillsProvider wires a snapshot func for /sessions/<sid>/skills (backs /skills). Formerly agent.WithAttachSkillsProvider.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL