Documentation
¶
Overview ¶
Package head turns durable thread messages into immediate conversational replies and asynchronous graph commands. It never plans or executes work; the thread remains responsive while the rest of codeaf changes the graph.
Index ¶
- Constants
- Variables
- func AbsorbStreamSession(sessionID string) string
- func AsideStreamSession(sessionID string) string
- func IsCorrection(instruction string) bool
- func LastCompilerAnswer(instruction string) (string, bool)
- func MarkRestartModel(instruction string) string
- func MarkTaskModel(instruction, model string) string
- func ReceiptStreamSession(sessionID string) string
- func RecognizesQualityIntent(instruction string) bool
- func RecognizesServiceIntent(instruction string) bool
- func RecognizesStandingIntent(instruction string) bool
- func SpliceCompilerAnswer(instruction, answer string) string
- func SpliceCorrection(words string, job store.Node, previous string, files []string, ...) string
- type Brief
- type Client
- type Compiler
- type Head
- func (h *Head) ApplyInterrupt(command store.Command) bool
- func (h *Head) Ask(ctx context.Context, sessionID, question string) (string, error)
- func (h *Head) BackfillRoomNames(ctx context.Context)
- func (h *Head) ConfirmSurgery(sessionID string, kind store.CommandKind, target string) (bool, error)
- func (h *Head) Interrupt(partial string) bool
- func (h *Head) RequestInterrupt(sessionID, reason, partial string) (InterruptRoute, error)
- func (h *Head) Serve(ctx context.Context) error
- func (h *Head) WithCompetenceMap(competence func() string) *Head
- func (h *Head) WithContextLength(tokens int) *Head
- func (h *Head) WithDailyBudgetUSD(amount float64) *Head
- func (h *Head) WithImageInput(modalities interface{ ... }, defaultModel string) *Head
- func (h *Head) WithMessageClient(selectClient func(store.Message) (Client, error)) *Head
- func (h *Head) WithRoomNaming(enabled bool) *Head
- func (h *Head) WithSelfKnowledge(knowledge func() string) *Head
- func (h *Head) WithStandingWatch(status func() string) *Head
- func (h *Head) WithWorkspace(root string) *Head
- type InterruptRoute
- type ModelResolver
- type ModelWords
- type PartList
- type WorkModelChoice
Constants ¶
const ( // ClassSetNameLimit is how many titles a set receipt names before it counts // the remainder. Three is a sentence; ten is a list. ClassSetNameLimit = 3 // ClassFallbackFloor is the score a lone content match must clear before it // may act on a message that also named a status class. It is redirection's // anchor floor — roughly one solid content word against a job's own brief — // because the doubt is the same one: does this sentence really point here? ClassFallbackFloor = RedirectAnchorScore )
const ( ScaleLookup = "lookup" ScaleTask = "task" ScaleProject = "project" )
Scale values the compiler may emit. ScaleTask is also the degradation target for anything unrecognised: the safe default shape is one worker.
const ( StructureEnumerates = "enumerates" StructureStratifies = "stratifies" StructureOneJudgment = "one_judgement" StructureSingleAct = "single_act" )
Structure values, the structural reading scale must follow from.
const ( // ModelSlotBoost is the slot a boost word resolves to, and ModelSlotWork is // the candidacy filter a named model is resolved inside — the same two // slots the model palette uses. ModelSlotBoost = "boost" ModelSlotWork = "work" // MaxModelCandidates bounds one ambiguity question. More options than this // is a list, not a choice. MaxModelCandidates = 4 )
Model words are read deterministically, exactly like standing and service intent: which model runs a job is the user's decision, not a provider's.
Two shapes exist. A SLOT word ("with the better model", "use the boost model", "with model 2") names the boost slot and always resolves, because the slot always has a model. A NAME word ("use gemini", "with the opus model") names a model and is resolved against the live catalog by the surface; only a name the ask marked explicitly — it said "model" — earns a receipt line when nothing matches, because a bare word that resolves to nothing was probably never a model word at all.
const ( // RestartModelPrefix marks the head's reading of the model words on a // restart. It is matched rather than reconstructed, so a change to the // wording makes the reader return nothing — today's behaviour — instead of // a wrong model. RestartModelPrefix = "Run this restart on:" // RestartModelBoost is what the prefix carries when the ask named the boost // slot rather than a model. The slot always resolves, so this always does. RestartModelBoost = "the boost model" )
RecognizeModelWords had exactly one call site — inside Compiler.Compile, which only a fresh splice reaches — so model words could create a new job and could never move an existing one. The two natural phrasings diverged completely: "redo that with the better model" fell through to the router and became a brand-new job in a brand-new workspace, while "rerun that with the better model" matched the restart cue, was handled deterministically, and re-ran the failure on the default slot. A restart is where escalation actually belongs — the work exists, the user watched it go wrong, and they are asking for a stronger hand on the same job.
store.Command has no model field and adding one is a store change this seam does not need: the instruction is a durable payload and the model words are already in it, verbatim. What the head adds is its own deterministic READING of them, on one marked line, in exactly the idiom CompilerAnswerPrefix already uses. The head cannot resolve a name — the catalog lives with the surface that owns the slots — so it says what it read and leaves resolution where the resolver is.
const ( // RedirectAnchorScore is the BM25 floor a live job must clear before the // user's words are read as steering for it. It sits at roughly one solid // content-word match against a job's own brief: below that the overlap is // as likely to be a coincidence of vocabulary as a reference to the work, // and a wrong redirection edits a plan the user never meant to touch. RedirectAnchorScore = 0.4 // RedirectCandidateLimit bounds the disambiguation question. More than a // handful of choices is not a question, it is a list. RedirectCandidateLimit = 4 // AdjacencyMessageWindow is how far back the thread is read for the job // that spoke last. It is the floor of the router's own thread window — the // count the prompt is guaranteed to still carry whatever the big-step // truncation has done — because a line the model still carries is a line // the user can still be answering, and anything further back is history. AdjacencyMessageWindow = threadWindowKeep // AdjacencyQuiet is how long a job's own line stays the thing being // answered. A running job speaks on a two-minute heartbeat, so two of them // is the span in which nothing newer has been said; past that the thread // has moved on and mere position proves nothing about what the user means. AdjacencyQuiet = 4 * time.Minute )
const ( // SurgerySpendGateUSD is recorded spend above which cancel/restart needs // explicit consent. SurgerySpendGateUSD = store.SurgerySpendGateUSD // SurgeryRuntimeGate is live runtime above which cancellation needs consent. SurgeryRuntimeGate = store.SurgeryRuntimeGate // SurgeryCascadeGateNodes gates every operation that affects a larger tree. SurgeryCascadeGateNodes = store.SurgeryCascadeGateNodes )
const AsideStub = "▸ asked → answered"
AsideStub opens the collapsed line. It is a constant so a surface can recognize the row it must draw folded, and so the stub reads the same in the journal, in a search result and on screen.
const ( // BoardRowCap bounds every board read. A board longer than this is a log, // not a board: the model reads it to choose a target, and a dozen live jobs // is already more than a person holds in their head at once. // // It is the floor rather than the cap now. The board's rows are what the // board's bytes are spent on, so a head whose board block grew gets rows in // the same proportion (budget.go) — and a head that was told nothing about // its window gets exactly this dozen, which is what every board rendered // before the window was a fact anybody here could reach. BoardRowCap = 12 )
const CompilerAnswerPrefix = "Answer to compiler question:"
CompilerAnswerPrefix marks the user's answer where the head splices it back onto the instruction that raised the question. It is the rail's durable record that an ask was already put to the user: every deterministic recognizer reads the last answer as authoritative and none may raise that question again, because a recognizer that re-reads the older words asks the same question forever.
const ( // CorrectionPrefix marks the block a correction splice carries. The // resident reads it to know that this splice is a revision of its target // rather than a new job that merely follows one. CorrectionPrefix = "Correcting delivered work:" )
const CorrectionQuiet = 20 * time.Minute
CorrectionQuiet bounds adjacency once more than one delivered job is in the window. With a single settled job, position needs no clock: a result is the last word until the user answers it, however long they take to read it, and that argument is still exactly right. With four, the last node-anchored line is as likely to be some other job's heartbeat as the deliverable being corrected — so stale position stops being evidence and becomes a question.
const ForkedContextPrefix = store.ForkedContextPrefix
ForkedContextPrefix opens the inherited block in a pre-split instruction. It is the store's constant because the store is what reads old journal rows, and it stays named here because this is where the fence was minted for as long as there was a fence to mint.
NOTHING WRITES IT ANY MORE. The two halves travel as two typed fields on the command (store.Command.Instruction and .Context), which is the whole of this fix: a fence in one string is only a fence to a reader that looks for it, and every reader downstream was reading the string as the person's own words. The composition still exists — store.Command.Brief — but it exists for the ONE consumer that is entitled to both halves, the compiler.
const HeadInterruptKind = store.CommandHeadInterrupt
HeadInterruptKind is the command kind a journaled turn-cancel carries. It is an alias rather than a second spelling: the store owns the kind list, and two constants holding one string is how a kind quietly becomes two kinds.
const NoGlossNote = "The compiler supplied no reading of its own, so your request stands as the goal, word for word."
NoGlossNote is the receipt line for a compile whose goal came back blank. It is a constant so the surfaces that show it and the tests that pin it read one spelling.
const TaskModelPrefix = "Run this on:"
TaskModelPrefix marks the model a task tool's own `model` argument named. It is the RestartModelPrefix idiom for the commissioning half: store.Command has no model column, the instruction is the durable payload, and the head cannot resolve a name because the catalog lives with the surface that owns the slots. So the head writes down what was asked for and leaves resolution there.
Variables ¶
var ErrAsideEmpty = errors.New("head aside: nothing was asked")
ErrAsideEmpty says the aside carried no question.
var ErrInterruptUnreachable = errors.New("head interrupt: no route to the turn in flight")
ErrInterruptUnreachable is returned when neither route exists.
Functions ¶
func AbsorbStreamSession ¶
AbsorbStreamSession is the stream key the absorption turn's deltas carry.
func AsideStreamSession ¶
AsideStreamSession is the stream key an ephemeral turn's deltas carry.
func IsCorrection ¶
IsCorrection reports that a splice is a revision of the work it targets. Exported for the resident half, which owns what happens next.
func LastCompilerAnswer ¶
LastCompilerAnswer returns the most recent answer spliced onto instruction. Answers accumulate in order, so the last one is the live decision.
func MarkRestartModel ¶
MarkRestartModel appends the head's reading of a restart's model words to the instruction it journals. An instruction that names no model comes back byte-identical, so every restart that was silent stays silent.
func MarkTaskModel ¶
MarkTaskModel appends the head's reading of a task's model words. An empty argument comes back byte-identical, so every task that named no model stays exactly what the person said.
func ReceiptStreamSession ¶
ReceiptStreamSession is the stream key the wake's deltas carry.
func RecognizesQualityIntent ¶
RecognizesQualityIntent reads the user asking for quality rather than for routine work. It is the signal media tools need to reach for "best".
func RecognizesServiceIntent ¶
RecognizesServiceIntent marks asks whose requested end-state is a running thing the user can continue to open or use. Ordinary "run tests" work is deliberately excluded.
func RecognizesStandingIntent ¶
RecognizesStandingIntent is the compiler's temporal reading: true means the ask survives one completion and therefore must cross ratification.
func SpliceCompilerAnswer ¶
SpliceCompilerAnswer is the one way an answer rejoins its instruction.
func SpliceCorrection ¶
func SpliceCorrection(words string, job store.Node, previous string, files []string, previousBytes int) string
SpliceCorrection is the one way a correction is written down. The user's words lead and are never touched — the same law every other instruction obeys — and the deterministic block follows, in the idiom attached documents and quality words already use: a compiler that ignores the prompt cannot lose what the sentence was about.
previousBytes is how much of the last version travels; a non-positive value is the literal this file argues for. It is the caller's number because the caller is the head, and how much of a deliverable fits in a brief is a question about the window the head is speaking through (budget.go).
Types ¶
type Brief ¶
type Brief struct {
Goal string `json:"goal"`
Assumptions []string `json:"assumptions"`
// Constraints are the rules the request states about what the run may or
// may not DO, in the person's own words.
//
// They are a field rather than prose because prose is not something a gate
// can hold anything to. "Change no files" used to survive only inside the
// goal and inside the working method, and the run that was told it ran the
// command it was asked for, reported the line it was asked for, and was
// then sent back by its own review to write a test file (#427). The field
// travels to every spec of the job, is shown to every worker first, and is
// held against the workspace's own before-and-after list at the gate. See
// plan.Constraint and keepStatedConstraints, which is what keeps the field
// to the person's words rather than the compiler's.
Constraints []plan.Constraint `json:"constraints,omitempty"`
// Title is the rail-sized display name for the job, produced by the one
// call that has already read the whole ask. It used to be a second model
// round-trip of its own — measured at 222-330 prompt tokens for a 5-token
// answer, ~0.6s of the critical path before any leaf could start — for a
// label nothing downstream waits on. Empty is a valid answer and the
// caller falls back to whatever it named jobs with before.
Title string `json:"title,omitempty"`
// Scale is the compiler's honest judgement of shape: "lookup" (one fact,
// one step), "task" (one worker end to end), or "project" (structured work
// worth a planning pass). Downstream decides what to do with it; an
// unrecognised value degrades to task.
Scale string `json:"scale"`
// Structure is the structural reading scale is supposed to follow from,
// asked for in its own field so that it is ANSWERED before the label is
// chosen rather than reconstructed to justify one.
//
// The prompt already said "scale is that judgement's answer, not a label
// chosen first", and measured live that is exactly what went wrong: on an
// ask that both enumerates and stratifies ("a short note on each one first,
// and then one comparison table that uses all three notes") the compiler
// returned "task" three times out of three — and the SAME model, on the
// same prompt and the same sentence, returned "project" both times it was
// additionally asked to quote the rule it was following. The judgement was
// available; nothing made the model reach it before naming a label. So the
// reading is a field, and reconcileScale below makes the label follow it.
Structure string `json:"structure,omitempty"`
// Contract is the working method for a task-scale job, written by the one
// call that has already read the whole ask. It used to be a second
// structuring round-trip serialized between compile and dispatch — a paid
// call on the critical path of every single-worker job. Empty is a valid
// answer and the caller falls back to that separate pass.
Contract string `json:"contract,omitempty"`
// Parts is the compiler's reading of how many separate answers the ask
// wants: several requests in one breath that do not feed each other, each
// written as a complete standalone assignment. The reading is made here,
// where the whole ask was read; what shape the work then takes is decided
// downstream by the planner, which is handed these words verbatim as
// evidence and nothing more. There was once a route that took this field
// as a layout instead, and its whole failure was that a layout cannot say
// a request waits. Empty means one thing comes back.
Parts PartList `json:"parts,omitempty"`
// BuildsOn names earlier jobs this instruction continues or improves.
// The reconciler turns each into a real dependency edge, so the prior
// result flows to the new workers as an input digest instead of being
// rediscovered or guessed at.
BuildsOn []string `json:"builds_on"`
// Question is the one gap too consequential to guess, when one exists.
// Empty is the overwhelmingly common, correct value: asking is reserved
// for irreversible or expensive mistakes, never for preferences.
Question string `json:"question"`
// TrialOf is the fact sequence of the retrieved unsettled pair that this
// brief deliberately compares. Zero means no experiment was shaped.
TrialOf int64 `json:"trial_of"`
// QuestionOptions is the generic selectable askback surface. Charter is set
// only by the temporal compiler; both omit cleanly for ordinary work.
QuestionOptions []store.QuestionOption `json:"question_options,omitempty"`
Charter *store.CharterSpec `json:"charter,omitempty"`
// ServiceIntent is deterministic consent provenance; the provider never
// gets to infer whether a process may outlive its leaf.
ServiceIntent bool `json:"service_intent,omitempty"`
// WorkModel is the model the user named for this job in their own words.
// Empty is the ordinary case: the surface's current work model serves.
WorkModel string `json:"work_model,omitempty"`
// ModelNote is the one calm receipt line about that choice — which model
// runs the job, or why the name they used did not land.
ModelNote string `json:"model_note,omitempty"`
// Note is the one calm receipt line the compiler itself adds, when it has
// something to own up to: today, that it supplied no reading of its own and
// the person's words stand as the goal. It is never read from the wire —
// a model may not write the receipt about its own answer.
Note string `json:"-"`
}
Brief is the complete, assumption-bearing intent handed to planning.
It carried a Deliverable and a Budget until this wave, and neither had a reader anywhere in the tree: the goal already names the deliverable, planning takes its money from measured history, and resident_build copies eleven fields across and dropped exactly these two. What they did have was a validator that hard-rejected an empty one with no retry, so an otherwise perfect request came back as "I couldn't apply that request: compile request: empty budget" for a field nothing would have consumed. A validated field with no consumer is a pure failure source, so both are gone from the schema and from validation.
type Client ¶
type Client interface {
CompleteWithMessages(ctx context.Context, messages []ai.Message, options ...ai.Option) (*ai.Response, error)
}
Client is the one provider operation the conversational components need. Keeping the boundary this small makes both routing and compiling testable without a network.
type Compiler ¶
type Compiler struct {
// contains filtered or unexported fields
}
Compiler converts verbatim user intent into a planning brief without asking the user to resolve unspecified details first.
func NewCompiler ¶
NewCompiler returns an intent compiler backed by client.
func (*Compiler) Compile ¶
func (c *Compiler) Compile(ctx context.Context, instruction string, graphContext string) (Brief, error)
Compile applies assume-and-declare once. The exact instruction is appended deterministically to Goal even if a provider ignores that prompt rule, so no downstream transformation can silently lose the user's words.
func (*Compiler) WithModelResolver ¶
func (c *Compiler) WithModelResolver(resolve ModelResolver) *Compiler
WithModelResolver installs the surface's catalog-backed reading of model words. Without it the compiler still recognizes them and still says nothing wrong: every job simply runs on the default work model.
func (*Compiler) WithOneShotErrands ¶
WithOneShotErrands says every ask this compiler will ever see arrived on a surface that runs exactly one errand and then exits — `codeaf do`.
This is the surface stating a fact about itself, not an opinion about the work. "Once, not standing" is an option on the ratification card because a person may want it; a person who typed `codeaf do "<task>"` has already chosen it, in the verb, before the compiler read a word. Asking them again is asking a question into a process with nobody at the keyboard, and the live defect it caused was total: "flag every discrepancy" tripped the temporal recognizer's `every <word>` cue, a plain reconciliation of two CSVs was drafted as a standing rule with an invented two-minute cadence, and the run exited in three seconds having done none of the work it was sent to do.
So the temporal route is not taken here at all, and the ordinary compiler is told what surface it is compiling for. Nothing about the judgement of the WORK changes; the classification that changes is the one the surface already answered.
type Head ¶
type Head struct {
// contains filtered or unexported fields
}
Head tails the durable thread and turns each new user message into one fast routing call, one reply, and at most one asynchronous command.
func New ¶
New returns a conversational head backed by graphStore. The window is unknown until a surface says otherwise, and unknown means every block renders at the literal it shipped with (budget.go).
func (*Head) ApplyInterrupt ¶
ApplyInterrupt is the reconciler's arm, written here so the resident's edit is one line rather than a design. It reports whether there was a turn to stop, which is what the reconciler journals as the command's resolution.
The partial is deliberately not carried on the command: what the reader had seen is a property of the surface that was watching, not of the journal, and a stop arriving from a headless caller has no partial to carry. The turn's own machinery already keeps whatever the in-process path handed it.
func (*Head) Ask ¶
Ask runs one ephemeral turn and journals its collapsed stub.
It deliberately does NOT touch the turn machinery: turnCancel, turnFold and the partial belong to the conversation's own turn, and an aside asked while one is in flight must leave it exactly as it found it. That is 8.2.9's "the in-flight turn undisturbed", and it is why nothing here locks turnMu.
func (*Head) BackfillRoomNames ¶
BackfillRoomNames names the rooms that existed before the scribe did.
The post-turn clerk only ever fires at the end of a turn, so every room whose last turn was already over when it landed stays "untitled room" forever, however much conversation is in it. That is the reported bug, and it is not a naming failure — it is a room that has never had a moment when naming was anyone's job. This is that moment, once per launch.
It is the same pass, not a second one: same claim, same read of what the room is about, same model rung, same normalizer, same one-per-room guard. A room somebody named by hand is never touched, because a title IS the record that this has run. And it happens off the launch entirely — the caller gets a goroutine and its own turn back — so a window opens at the speed it always did whether the naming works, fails, or never finishes.
The rooms are named one after another rather than all at once. Eight concurrent provider calls at the exact moment a window is opening is the kind of thundering start that makes a launch feel slow through no fault of the thing the person is waiting for.
func (*Head) ConfirmSurgery ¶
func (h *Head) ConfirmSurgery(sessionID string, kind store.CommandKind, target string) (bool, error)
ConfirmSurgery is the confirm law offered to whoever else can journal a node command — today, the task page's single-key stop and restart.
The gates are the product's promise that nothing large is thrown away without somebody naming the loss out loud, and a keypress is not a smaller intention than a sentence. A key that journalled directly bought, for one accidental press, exactly what the conversational path has always had to ask for. So the key path asks this first: it reports true when it has asked, and the caller journals nothing — the durable question is the reply, and answering it replays the command through the same option machinery a spoken confirm does. False means the loss is small, and small work is simply done.
func (*Head) Interrupt ¶
Interrupt stops the turn being answered right now and carries in whatever of it the reader had already seen. It reports whether there was a turn to stop, so a surface that asked at the wrong moment can tell that nothing happened.
func (*Head) RequestInterrupt ¶
func (h *Head) RequestInterrupt(sessionID, reason, partial string) (InterruptRoute, error)
RequestInterrupt stops the head's turn in flight and reports how.
It journals first and on purpose: the funnel is the product's one authority path, and a stop that rode past it would be the second engine Part 3 forbids. Then it stops the turn in process, because until the reconciler's one case lands nothing drains that row, and a door that reports a stop while the turn carries on talking is worse than one that never claimed it.
The two roads are not a race. Both end at the same handle and the handle is idempotent: whichever arrives second finds no turn and says so.
The route reported is the durable one when the row landed, because that is the fact a surface needs — a journaled stop is replayable, reaches a headless caller and survives this process. NothingRunning is reserved for the case where neither road found anything at all to do.
partial is whatever of the turn the reader had already seen, so the transcript keeps the words that did arrive, marked where they stopped.
func (*Head) Serve ¶
Serve tails every session until ctx is cancelled.
On startup it resumes each session at that session's last non-user message. This intentionally replays only a trailing run of user messages: history ending in an agent or system message is treated as answered, while a crash after the user wrote but before the head replied remains recoverable. It is a deliberately simple journal rule; the cursor advances only after each message has been handled. The rule is asked per room because one number cannot state it for two — a reply in either would carry it past the other's unanswered rows, and those rows would never be read.
func (*Head) WithCompetenceMap ¶
WithCompetenceMap registers the derived capability view with the head's grounding path. It is read only for competence-shaped questions, and its structured data is voiced by the head's existing single routing call.
func (*Head) WithContextLength ¶
WithContextLength tells the head how much its talk model holds, in tokens.
It is a fact handed down rather than looked up, which is exec.Linear's own doctrine for the same question: the catalog belongs to the surface, and a package that reached for it would be a conversational head with an opinion about model metadata. Zero — a model the catalog cannot size, a visitor client that holds no catalog at all — is not read as small; it leaves the head exactly as New built it.
The budget is resolved here, once, for the life of the head.
func (*Head) WithDailyBudgetUSD ¶
WithDailyBudgetUSD lets the head render policy state and consume a pending rail question deterministically. Zero is unlimited.
func (*Head) WithImageInput ¶
func (h *Head) WithImageInput(modalities interface { Supports(string, string, string) bool }, defaultModel string) *Head
WithImageInput lets the routing head receive durable chat attachments as OpenAI-style image parts when its current talk model advertises vision.
func (*Head) WithMessageClient ¶
WithMessageClient selects a conversational client for one durable user message. Messages without an override continue through the Head's ordinary client; the callback is the single seam used by heavier chat lanes.
func (*Head) WithRoomNaming ¶
WithRoomNaming turns on the post-turn clerk that names rooms (scribe.go).
It is a switch rather than always-on because it costs a provider call the TURN did not ask for, and only one kind of window is buying anything with it: one that draws a list of rooms. A headless errand has no rail, one room, and nobody to read a name — so the default is off and the chat window says so out loud, which also keeps every measurement of a turn's cost a measurement of the turn.
func (*Head) WithSelfKnowledge ¶
WithSelfKnowledge supplies measured execution history to the routing call. Nil and empty values preserve the original prompt exactly.
func (*Head) WithStandingWatch ¶
WithStandingWatch registers the same calm status block used by doctor. It is read only for presence-shaped questions.
func (*Head) WithWorkspace ¶
WithWorkspace tells the head where artifacts are born.
It is a builder rather than a constant because where a chat writes is a property of the surface that started it: `codeaf chat` in a project means that project's directory, a room under rooms will mean the room's own. Unset, the head writes into the process's working directory — the same choice `codeaf do` already makes for an errand pointed at somebody's own folder, and the one a person typing "write me the diagram" in a terminal expects.
type InterruptRoute ¶
type InterruptRoute string
InterruptRoute says which way a stop actually travelled.
const ( // InterruptJournaled is the destination: the stop is a durable row, replayed // like everything else, reachable from any surface and from a headless caller. InterruptJournaled InterruptRoute = "journaled" // InterruptInProcess is today: the handle the TUI's escape key already holds. InterruptInProcess InterruptRoute = "in-process" // InterruptNothingRunning is not a failure. A surface that asked at the wrong // moment must be able to tell that nothing happened. InterruptNothingRunning InterruptRoute = "nothing-running" )
type ModelResolver ¶
type ModelResolver func(ModelWords) WorkModelChoice
ModelResolver resolves recognized model words against the live catalog and the surface's slots. Nil leaves every job on the default work model.
type ModelWords ¶
type ModelWords struct {
// Boost means the ask named the boost slot rather than a model.
Boost bool
// Names are candidate model words in the order they appeared. The surface
// resolves them against the catalog; the first that resolves wins.
Names []string
// Explicit means the ask said "model" beside the name, so an unresolvable
// name is worth one calm receipt line rather than silence.
Explicit bool
// Answered means these words were read out of the user's answer to a
// question this ask already asked. The choice is settled: whatever the
// catalog says, the job proceeds and nothing asks again.
Answered bool
}
ModelWords is the deterministic reading of the model words in a task ask.
func RecognizeModelWords ¶
func RecognizeModelWords(instruction string) (ModelWords, bool)
RecognizeModelWords reads a task ask for the model the user asked for. An ask that already carries an answer is read answer-first: the answer is younger than the words that raised the question, and reading those words again is how the same question came back forever.
func RestartModel ¶
func RestartModel(instruction string) (ModelWords, bool)
RestartModel reads back what MarkRestartModel wrote. The second return is whether the restart named a model at all; the first is the words to resolve, in the shape a ModelResolver already takes.
func TaskModel ¶
func TaskModel(instruction string) (ModelWords, bool)
TaskModel reads back what MarkTaskModel wrote.
type PartList ¶
type PartList []string
PartList tolerates the shapes models actually send when they list the ask's separate requests. The strict form is an array of strings; live models were measured wrapping each part in an object instead, and a declaration field must never be able to fail the compile — the worst legal outcome of a malformed parts array is no declaration, which is exactly what the field meant before it existed.
func (*PartList) UnmarshalJSON ¶
type WorkModelChoice ¶
type WorkModelChoice struct {
Model string
Candidates []string
// Requested is the word the user actually used, for the receipt.
Requested string
}
WorkModelChoice is the surface's answer about the model words in one ask. Exactly one of Model and Candidates is meaningful: a resolution, or the shortlist behind one choose question. Both empty means nothing matched.
Source Files
¶
- absorb.go
- activity.go
- acts.go
- artifact.go
- aside.go
- asks.go
- attribution.go
- bash.go
- brief.go
- budget.go
- change.go
- charters.go
- class.go
- coalesce.go
- compiler.go
- control.go
- correction.go
- cursors.go
- depth.go
- dictation.go
- fork.go
- head.go
- hints.go
- interrupt.go
- json.go
- lens.go
- live.go
- loop.go
- modelwords.go
- money.go
- narrow.go
- prompt.go
- question_identity.go
- revision.go
- revisionvoice.go
- scribe.go
- services.go
- split.go
- standing.go
- stop.go
- surgery.go
- targets.go
- task.go
- toolbelt.go
- transcript.go
- urgency.go
- wake.go
- write.go