cowork

package
v0.6.0 Latest Latest
Warning

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

Go to latest
Published: Sep 30, 2026 License: MIT Imports: 26 Imported by: 0

Documentation

Overview

Package cowork is live mode (docs/agents/live.md): the agent working in a running 012 session, as one of the participants of the session's room (package room). A session listens on a socket only its user can reach (Listen); 012 mcp --attach connects to it (Attach) and carries the agent's MCP messages to a server running in the session, whose tools stand on the room's workbook (Agent, an mcp.Live). What the agent reads is the workbook on the screen; what it changes is a proposal (sheet.Propose) on the room's Board, marked on the person's screen until they accept or reject it, or, when they let agents edit directly, a step of the agent's own. The Board also carries the agent's questions to the person (Ask) and what the person allows it.

Everything on the Board is the room's, so it is read and changed in a turn (room.Seat.Do), as the workbook is.

Index

Constants

View Source
const BoardKey = "agents"

BoardKey is the room value (room.Seat.Value) holding its Board.

Variables

View Source
var ErrGone = errors.New("that suggestion isn't waiting any more")

ErrGone is accepting or rejecting a suggestion no longer waiting.

View Source
var ErrReadOnly = errors.New("you were invited read only: you can read the workbook, point at cells and ask the person, but not change anything; ask them to invite you again with a scope that lets you")

ErrReadOnly refuses any change of the agent invited read only.

Functions

func Attach

func Attach(target string, in io.Reader, out io.Writer) error

Attach is 012 mcp --attach: it finds the session target names (see Pick), refusing a socket that isn't the user's alone, and carries the MCP client's messages from in to it and its answers to out until either side ends. The client's first message, its initialize request, names it to the others. When the session can't be reached, the initialize request is answered with the reason, which hosts show, and Attach returns it.

func SocketDir

func SocketDir() (string, error)

SocketDir is the folder sessions listen in.

Types

type Agent

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

Agent is an attached agent's side of the room: its seat, and the mcp.Live its tools stand on. Every call is a turn of its seat, so its reads and changes take their place among the people's, and its steps are its own.

func NewAgent

func NewAgent(seat *room.Seat) *Agent

NewAgent is the agent sitting at seat.

func (*Agent) Ask

func (a *Agent) Ask(ctx context.Context, p *sdk.ElicitParams) (*sdk.ElicitResult, error)

Ask puts the question on the board and waits for the person.

func (*Agent) Change

func (a *Agent) Change(ctx context.Context, label string, dryRun bool, fn func(*sheet.Workbook) error) ([]diff.Change, error)

Change is Make without a message.

func (*Agent) Focus

func (a *Agent) Focus(_ context.Context, ref string) (string, error)

Focus moves the agent's pointer to ref.

func (*Agent) Leave

func (a *Agent) Leave()

Leave takes the agent's questions off the board and gives up its seat. Its suggestions stay for the person to settle.

func (*Agent) Make

func (a *Agent) Make(ctx context.Context, mk mcp.Making) (mcp.Made, error)

Make proposes the change on a copy of the workbook, checks it against the scope, and then suggests it, or with direct edits allowed makes it as the agent's step; a formula asking JEV made directly asks the person first.

func (*Agent) Name

func (a *Agent) Name() string

Name is the workbook's.

func (*Agent) RunCell

func (a *Agent) RunCell(ctx context.Context, notebook string, cell int) (string, error)

RunCell asks leave to run a notebook cell, unless the person gave it for the session, and has the room's keeper run it.

func (*Agent) Scope

func (a *Agent) Scope() string

Scope is the board's scope in words.

func (*Agent) Scratch

func (a *Agent) Scratch(_ context.Context, fn func(*sheet.Workbook) error) error

Scratch runs fn on a copy of the workbook taken in a turn.

func (*Agent) Seat

func (a *Agent) Seat() *room.Seat

Seat is the agent's seat.

func (*Agent) Suggestions

func (a *Agent) Suggestions(context.Context) ([]mcp.Suggestion, error)

Suggestions lists the agent's suggestions.

func (*Agent) Undo

func (a *Agent) Undo(context.Context) (string, error)

Undo takes back the agent's latest step.

func (*Agent) View

func (a *Agent) View(_ context.Context, fn func(*sheet.Workbook) error) error

View runs fn on the room's workbook in a turn.

type Answer

type Answer struct {
	Action  string
	Content map[string]any
	Always  bool
}

Answer is the person's answer: the elicitation's action (accept, decline, cancel) and values by field, and for a grant whether it holds for the rest of the session.

type Ask

type Ask struct {
	ID      int
	Author  int // the agent's seat
	Agent   string
	Color   int
	Kind    AskKind
	Message string
	Fields  []Field
	// Grant is what a Grant asks for: "notebooks" or "jev".
	Grant string
	// contains filtered or unexported fields
}

Ask is a question waiting for a person's answer.

func NewAsk

func NewAsk(message string, schema any) (*Ask, error)

NewAsk makes a question, its fields read from an elicitation's requested schema (nil for a yes or no question).

func NewGrant

func NewGrant(what, message string) *Ask

NewGrant asks leave for what ("notebooks" or "jev").

func (*Ask) Done

func (a *Ask) Done() bool

Done reports whether the question was answered or withdrawn.

func (*Ask) Reply

func (a *Ask) Reply() <-chan Answer

Reply is where the answer arrives, once.

type AskKind

type AskKind int

AskKind is what a question is for.

const (
	// Question is the agent's own question (the ask tool).
	Question AskKind = iota
	// Grant asks leave to run notebook cells or enter JEV formulas.
	Grant
)

type Board

type Board struct {
	// Scope is what agents may change, as the person who invited them
	// chose; the whole workbook in 012 serve.
	Scope Scope
	// Direct lets agents' changes be made at once, as their own steps,
	// rather than suggested ("Let agents edit directly").
	Direct bool
	Grants Grants
	// Host is the seat of the person who invited the agents, who is
	// asked their questions; 0 in 012 serve, where any person is.
	Host int
	// contains filtered or unexported fields
}

Board is what a room shares of its agents: their suggestions, their questions to the people, and what the people let them do.

func BoardOf

func BoardOf(seat *room.Seat) *Board

BoardOf is the Board of seat's room, made the first time. Call it in a turn.

func (*Board) Accept

func (b *Board) Accept(w *sheet.Workbook, id int, cells []int) error

Accept makes the cells of suggestion id that are picked (nil: all it has pending) on w, as one step in the agent's name, and marks them accepted. A change made whole only is accepted whole.

func (*Board) Add

func (b *Board) Add(s *Suggestion) int

Add puts a suggestion on the board and numbers it.

func (*Board) AddAsk

func (b *Board) AddAsk(a *Ask)

AddAsk puts a question on the board for a person to answer.

func (*Board) Answer

func (b *Board) Answer(a *Ask, ans Answer)

Answer answers a question and takes it off the board.

func (*Board) Asks

func (b *Board) Asks() []*Ask

Asks are the questions waiting, oldest first.

func (*Board) At

func (b *Board) At(name string, a sheet.Addr) (*Suggestion, int, bool)

At is the newest pending suggestion setting the cell a of the sheet named name, and the index of the cell in it.

func (*Board) Get

func (b *Board) Get(id int) *Suggestion

Get is the suggestion numbered id.

func (*Board) NextAsk

func (b *Board) NextAsk(seat int, present func(id int) bool) *Ask

NextAsk is the first question waiting that the person at seat should answer: the host's, or in a room without one, anyone's not already shown to another who is here (present). It is then seat's to show.

func (*Board) Of

func (b *Board) Of(author int) []*Suggestion

Of are the suggestions agent's seat made, oldest first.

func (*Board) Pending

func (b *Board) Pending() []*Suggestion

Pending are the suggestions still waiting, oldest first.

func (*Board) Reject

func (b *Board) Reject(id int, cells []int) error

Reject marks the cells of suggestion id picked (nil: all it has pending) rejected, changing nothing.

func (*Board) Resolved

func (b *Board) Resolved(author int) []*Suggestion

Resolved are the agent's suggestions settled since it last asked, which it hasn't been told of.

func (*Board) Withdraw

func (b *Board) Withdraw(a *Ask)

Withdraw takes a question off the board unanswered: the agent gave up waiting, or left.

type CellState

type CellState int

CellState is what became of one cell of a suggestion.

const (
	CellPending CellState = iota
	CellAccepted
	CellRejected
)

type Endpoint

type Endpoint struct {
	PID    int    `json:"pid"`
	Socket string `json:"socket"`
	// Kind is "session" for a 012 on a terminal, "serve" for 012 serve,
	// whose agents name the file they join.
	Kind string `json:"kind"`
	// Workbook is the session's workbook, as its title names it, or the
	// folder 012 serve serves.
	Workbook string    `json:"workbook"`
	Started  time.Time `json:"started"`
}

Endpoint is what a listening session says of itself.

func Endpoints

func Endpoints(dir string) ([]Endpoint, error)

Endpoints are the sessions listening in dir, newest first. Files left by sessions that are gone are removed.

func Pick

func Pick(eps []Endpoint, target string) (Endpoint, string, error)

Pick is the endpoint target names among eps, and the file the agent of 012 serve joins: "" when only one session listens, a workbook's name, a pid, a socket's path, or with one 012 serve listening, the file to join.

type Field

type Field struct {
	Name, Title, Description string
	Kind                     FieldKind
	Choices                  []string // a Choice's values
	Labels                   []string // and how they're shown, when the schema names them
	Required                 bool
}

Field is one thing a question asks for.

func Fields

func Fields(schema any) ([]Field, error)

Fields reads an elicitation's requested schema: an object of top-level string, number, integer and boolean properties, strings with enum (or oneOf consts) being a choice, as MCP allows.

func (Field) ChoiceLabel

func (f Field) ChoiceLabel(i int) string

ChoiceLabel is how choice i is shown.

func (Field) Label

func (f Field) Label() string

Label is how the field is named to the person.

func (Field) Parse

func (f Field) Parse(text string) (any, error)

Parse reads what the person typed for a Text, Number or Integer field.

type FieldKind

type FieldKind int

FieldKind is the type of what a field asks for.

const (
	Text FieldKind = iota
	Number
	Integer
	Boolean
	Choice
)

type Grants

type Grants struct {
	Notebooks bool
	JEV       bool
}

Grants are what the person lets agents do beyond changing cells, for the session: run notebook cells with nu, and enter formulas that ask a hosted model over the network (JEV) directly, as untrusted macros ask before either. Without a grant, the agent's request asks the person first.

type Listener

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

Listener is a session listening for agents.

func Listen

func Listen(o Options) (*Listener, error)

Listen starts listening for agents, on a socket only the user can reach.

func (*Listener) Close

func (l *Listener) Close() error

Close stops listening and ends every agent's connection; they leave their rooms once their servers stop, which Wait waits for. Close may be called in a turn: it takes no room's lock.

func (*Listener) Socket

func (l *Listener) Socket() string

Socket is the socket's path.

func (*Listener) Wait

func (l *Listener) Wait()

Wait waits for the connections to end after Close. It takes rooms' locks, so not in a turn.

type Options

type Options struct {
	Registry *room.Registry
	// Room is the key of the room the agent joins, given the name its
	// attach gave ("" for none): a session's own, whatever the name, or
	// in 012 serve the file named, which someone must have open.
	Room func(name string) (string, error)
	// Kind and Workbook are what the endpoint file says (Endpoint).
	Kind, Workbook string
	// Version is 012's, which the MCP server reports.
	Version string
	// Dir is the folder to listen in; "" is SocketDir.
	Dir string
}

Options say what a listener serves.

type RunCell

type RunCell struct {
	Notebook string
	Cell     int // from 0
	Agent    string
}

RunCell is a request for the keeper's session to run a notebook cell, which it takes from the room (room.Seat.Post).

type Scope

type Scope struct {
	Kind  ScopeKind
	Sheet *sheet.Sheet // OneSheet, OneRange
	Range sheet.Rect   // OneRange
}

Scope is what the person invited the agent to change. The agent reads the whole workbook whatever the scope, as formulas in it read cells outside it; the scope bounds its changes and where it points.

func (Scope) Allows

func (s Scope) Allows(sh *sheet.Sheet, r sheet.Rect) bool

Allows reports whether the agent may point at r of sheet s.

func (Scope) Check

func (s Scope) Check(p *sheet.Proposal) error

Check refuses a proposal that changes anything outside the scope, saying what.

func (Scope) String

func (s Scope) String() string

String is the scope in words: "the whole workbook", "sheet Q3", "Q3!A1:C9", "read only".

type ScopeKind

type ScopeKind int

ScopeKind is how much of the workbook the agent may change.

const (
	// Workbook lets the agent change anything.
	Workbook ScopeKind = iota
	// OneSheet lets it change one sheet's cells, lines and charts.
	OneSheet
	// OneRange lets it change the cells of one range.
	OneRange
	// ReadOnly lets it change nothing.
	ReadOnly
)

type Suggestion

type Suggestion struct {
	ID      int
	Author  int    // the agent's seat
	Agent   string // its name
	Color   int
	Message string
	At      time.Time
	*sheet.Proposal
	// States are its cells', in the order of Proposal.Cells; a change
	// made whole only has one, for all of it.
	States []CellState
	// Failed is why accepting it failed, when it did.
	Failed string
	// contains filtered or unexported fields
}

Suggestion is one of the agents's change waiting for a person: the proposal, who made it and why, and what became of each of its cells.

func (*Suggestion) Count

func (s *Suggestion) Count(st CellState) int

Count is how many of its cells are in state st.

func (*Suggestion) Notice

func (s *Suggestion) Notice() string

Notice is what the agent is told of a settled suggestion.

func (*Suggestion) Outcome

func (s *Suggestion) Outcome() string

Outcome is what became of it in words: pending, accepted, accepted in part or rejected.

func (*Suggestion) Pending

func (s *Suggestion) Pending() bool

Pending reports whether any of it waits for the person.

Jump to

Keyboard shortcuts

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