snapshot of https://github.com/tldraw/tldraw.git (main, 2026-09-23; blobs > 131072B omitted)
Some checks failed
Deploy analytics worker / Deploy analytics worker to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
i18n upload strings / Upload latest strings (push) Failing after 1s
Deploy bemo / Deploy bemo to ${{ (github.ref == 'refs/heads/bemo-production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
Deploy grafana resources / ${{ github.event_name == 'push' && 'Deploy grafana resources' || 'Validate grafana resources (dry run)' }} (push) Failing after 0s
End to end tests (dotcom) / Check for relevant changes (push) Failing after 0s
End to end tests (examples) / Check for relevant changes (push) Failing after 0s
End to end tests (mermaid readability) / Check for relevant changes (push) Failing after 1s
End to end tests (perf) / Check for relevant changes (push) Failing after 0s
Publish editor extensions / Publish editor extensions (push) Failing after 1s
Publish SDK packages / Remove the publish-packages label (push) Has been skipped
Checks / Tests & checks (push) Has been cancelled
Checks / Build all projects (push) Has been cancelled
Deploy templates / Deploy templates (push) Has been cancelled
End to end tests (dotcom) / End to end tests (dotcom) (push) Has been cancelled
End to end tests (mermaid readability) / End to end tests (mermaid readability) (push) Has been cancelled
End to end tests (perf) / End to end tests (perf) (push) Has been cancelled
Deploy .com / Deploy dotcom to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Has been cancelled
End to end tests (examples) / End to end tests (examples) (push) Has been cancelled
Publish SDK packages / Publish SDK packages (push) Has been cancelled
Publish SDK packages / Tell user released version (push) Has been cancelled
Publish SDK packages / Publishes code templates to separate repositories (push) Has been cancelled
Publish SDK packages / Deploys the template demos with the new SDK version (push) Has been cancelled
Some checks failed
Deploy analytics worker / Deploy analytics worker to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
i18n upload strings / Upload latest strings (push) Failing after 1s
Deploy bemo / Deploy bemo to ${{ (github.ref == 'refs/heads/bemo-production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
Deploy grafana resources / ${{ github.event_name == 'push' && 'Deploy grafana resources' || 'Validate grafana resources (dry run)' }} (push) Failing after 0s
End to end tests (dotcom) / Check for relevant changes (push) Failing after 0s
End to end tests (examples) / Check for relevant changes (push) Failing after 0s
End to end tests (mermaid readability) / Check for relevant changes (push) Failing after 1s
End to end tests (perf) / Check for relevant changes (push) Failing after 0s
Publish editor extensions / Publish editor extensions (push) Failing after 1s
Publish SDK packages / Remove the publish-packages label (push) Has been skipped
Checks / Tests & checks (push) Has been cancelled
Checks / Build all projects (push) Has been cancelled
Deploy templates / Deploy templates (push) Has been cancelled
End to end tests (dotcom) / End to end tests (dotcom) (push) Has been cancelled
End to end tests (mermaid readability) / End to end tests (mermaid readability) (push) Has been cancelled
End to end tests (perf) / End to end tests (perf) (push) Has been cancelled
Deploy .com / Deploy dotcom to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Has been cancelled
End to end tests (examples) / End to end tests (examples) (push) Has been cancelled
Publish SDK packages / Publish SDK packages (push) Has been cancelled
Publish SDK packages / Tell user released version (push) Has been cancelled
Publish SDK packages / Publishes code templates to separate repositories (push) Has been cancelled
Publish SDK packages / Deploys the template demos with the new SDK version (push) Has been cancelled
This commit is contained in:
107
templates/agent/client/modes/AgentModeChart.ts
Normal file
107
templates/agent/client/modes/AgentModeChart.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
import type { AgentRequest } from '../../shared/types/AgentRequest'
|
||||
import type { TldrawAgent } from '../agent/TldrawAgent'
|
||||
import type { AgentModeDefinition, AgentModeType } from './AgentModeDefinitions'
|
||||
|
||||
/**
|
||||
* Lifecycle hooks for an agent mode.
|
||||
* Each mode can optionally implement these hooks to respond to state changes.
|
||||
*/
|
||||
export interface AgentModeNode {
|
||||
onEnter?(agent: TldrawAgent, fromMode: AgentModeType): void
|
||||
onExit?(agent: TldrawAgent, toMode: AgentModeType): void
|
||||
onPromptStart?(agent: TldrawAgent, request: AgentRequest): void
|
||||
onPromptEnd?(agent: TldrawAgent, request: AgentRequest): void
|
||||
onPromptCancel?(agent: TldrawAgent, request: AgentRequest): void
|
||||
}
|
||||
|
||||
/**
|
||||
* Lifecycle implementations for each agent mode.
|
||||
*
|
||||
* This chart maps mode types to their lifecycle hooks.
|
||||
* Modes can implement any subset of hooks (all are optional).
|
||||
* Not all modes need an entry - modes without entries simply have no lifecycle behavior.
|
||||
*
|
||||
* To add lifecycle behavior for a new mode:
|
||||
* 1. Add the mode to AGENT_MODE_DEFINITIONS in AgentModeDefinitions.ts
|
||||
* 2. Add an entry here with the lifecycle hooks you need
|
||||
*/
|
||||
const _AGENT_MODE_CHART: Record<AgentModeDefinition['type'], AgentModeNode> = {
|
||||
idling: {
|
||||
onPromptStart(agent) {
|
||||
agent.mode.setMode('working')
|
||||
},
|
||||
onEnter(agent, _fromMode) {
|
||||
agent.todos.reset()
|
||||
agent.userAction.clearHistory()
|
||||
},
|
||||
},
|
||||
working: {
|
||||
onEnter(agent, fromMode) {
|
||||
// Reset state when entering working mode
|
||||
agent.todos.reset()
|
||||
// agent.userAction.clearHistory()
|
||||
agent.context.clear()
|
||||
|
||||
// When entering working mode from idling, clear created shapes tracking
|
||||
// This handles the case where a user prompt starts while in idling mode,
|
||||
// which transitions to working before working.onPromptStart is called
|
||||
if (fromMode === 'idling') {
|
||||
agent.lints.clearCreatedShapes()
|
||||
}
|
||||
},
|
||||
|
||||
onExit(agent, _toMode) {
|
||||
// Unlock all shapes created during the prompt when exiting working mode
|
||||
agent.lints.unlockCreatedShapes()
|
||||
},
|
||||
|
||||
onPromptStart(agent, request) {
|
||||
// Clear created shapes tracking and flush todos when a new user prompt starts
|
||||
// This handles cases where a prompt starts while already in working mode (e.g., continuation, interrupt)
|
||||
if (request.source === 'user') {
|
||||
agent.todos.flush()
|
||||
agent.lints.clearCreatedShapes()
|
||||
}
|
||||
},
|
||||
|
||||
onPromptEnd(agent, _request) {
|
||||
// Check if there are incomplete todos
|
||||
const todoList = agent.todos.getTodos()
|
||||
const incompleteTodos = todoList.filter((item) => item.status !== 'done')
|
||||
|
||||
if (incompleteTodos.length > 0) {
|
||||
// Schedule continuation to complete remaining work
|
||||
agent.schedule(
|
||||
"Continue until all your todo items are marked as done. If you've completed the work, mark them as done, otherwise keep going."
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// Check if there are unsurfaced lints on created shapes
|
||||
if (agent.lints.hasUnsurfacedLints(agent.lints.getCreatedShapes())) {
|
||||
agent.schedule({
|
||||
agentMessages: [
|
||||
'The automated linter has detected potential visual problems in the canvas. Decide if they need to be addressed.',
|
||||
],
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// All work complete - return to idling
|
||||
agent.mode.setMode('idling')
|
||||
},
|
||||
|
||||
onPromptCancel(agent, _request) {
|
||||
// Return to idling on cancel
|
||||
agent.mode.setMode('idling')
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the lifecycle node for a mode, if one exists.
|
||||
* This function helps TypeScript resolve types correctly with circular imports.
|
||||
*/
|
||||
export function getModeNode(mode: AgentModeType): AgentModeNode {
|
||||
return _AGENT_MODE_CHART[mode]
|
||||
}
|
||||
194
templates/agent/client/modes/AgentModeDefinitions.ts
Normal file
194
templates/agent/client/modes/AgentModeDefinitions.ts
Normal file
@@ -0,0 +1,194 @@
|
||||
import type { AgentAction } from '../../shared/types/AgentAction'
|
||||
import type { PromptPart } from '../../shared/types/PromptPart'
|
||||
// Import action utils to ensure they register themselves
|
||||
import { AddDetailActionUtil } from '../actions/AddDetailActionUtil'
|
||||
import { AlignActionUtil } from '../actions/AlignActionUtil'
|
||||
import { BringToFrontActionUtil } from '../actions/BringToFrontActionUtil'
|
||||
import { ClearActionUtil } from '../actions/ClearActionUtil'
|
||||
import { CountryInfoActionUtil } from '../actions/CountryInfoActionUtil'
|
||||
import { CountShapesActionUtil } from '../actions/CountShapesActionUtil'
|
||||
import { CreateActionUtil } from '../actions/CreateActionUtil'
|
||||
import { DeleteActionUtil } from '../actions/DeleteActionUtil'
|
||||
import { DistributeActionUtil } from '../actions/DistributeActionUtil'
|
||||
import { LabelActionUtil } from '../actions/LabelActionUtil'
|
||||
import { MessageActionUtil } from '../actions/MessageActionUtil'
|
||||
import { MoveActionUtil } from '../actions/MoveActionUtil'
|
||||
import { PenActionUtil } from '../actions/PenActionUtil'
|
||||
import { PlaceActionUtil } from '../actions/PlaceActionUtil'
|
||||
import { ResizeActionUtil } from '../actions/ResizeActionUtil'
|
||||
import { ReviewActionUtil } from '../actions/ReviewActionUtil'
|
||||
import { RotateActionUtil } from '../actions/RotateActionUtil'
|
||||
import { SendToBackActionUtil } from '../actions/SendToBackActionUtil'
|
||||
import { SetMyViewActionUtil } from '../actions/SetMyViewActionUtil'
|
||||
import { StackActionUtil } from '../actions/StackActionUtil'
|
||||
import { ThinkActionUtil } from '../actions/ThinkActionUtil'
|
||||
import { UnknownActionUtil } from '../actions/UnknownActionUtil'
|
||||
import { UpdateActionUtil } from '../actions/UpdateActionUtil'
|
||||
import { UpsertTodoListItemActionUtil } from '../actions/UpsertTodoListItemActionUtil'
|
||||
// Import prompt part utils to ensure they register themselves
|
||||
import { AgentViewportBoundsPartUtil } from '../parts/AgentViewportBoundsPartUtil'
|
||||
import { BlurryShapesPartUtil } from '../parts/BlurryShapesPartUtil'
|
||||
import { CanvasLintsPartUtil } from '../parts/CanvasLintsPartUtil'
|
||||
import { ChatHistoryPartUtil } from '../parts/ChatHistoryPartUtil'
|
||||
import { ContextItemsPartUtil } from '../parts/ContextItemsPartUtil'
|
||||
import { DataPartUtil } from '../parts/DataPartUtil'
|
||||
import { DebugPartUtil } from '../parts/DebugPartUtil'
|
||||
import { MessagesPartUtil } from '../parts/MessagesPartUtil'
|
||||
import { ModelNamePartUtil } from '../parts/ModelNamePartUtil'
|
||||
import { ModePartUtil } from '../parts/ModePartUtil'
|
||||
import { PeripheralShapesPartUtil } from '../parts/PeripheralShapesPartUtil'
|
||||
import { ScreenshotPartUtil } from '../parts/ScreenshotPartUtil'
|
||||
import { SelectedShapesPartUtil } from '../parts/SelectedShapesPartUtil'
|
||||
import { TimePartUtil } from '../parts/TimePartUtil'
|
||||
import { TodoListPartUtil } from '../parts/TodoListPartUtil'
|
||||
import { UserActionHistoryPartUtil } from '../parts/UserActionHistoryPartUtil'
|
||||
import { UserViewportBoundsPartUtil } from '../parts/UserViewportBoundsPartUtil'
|
||||
|
||||
/**
|
||||
* What an agent can see and do when in a given mode.
|
||||
*
|
||||
* This is a discriminated union based on the `active` property:
|
||||
* - Active modes can take actions and receive prompt parts
|
||||
* - Inactive modes cannot take actions (agent is idle/waiting)
|
||||
*/
|
||||
export type AgentModeDefinition = {
|
||||
/** A unique identifier for the agent mode. */
|
||||
type: string
|
||||
} & (
|
||||
| {
|
||||
/** Whether the agent is active in this mode and can take actions. */
|
||||
active: true
|
||||
/** The prompt parts that determine what information will be sent to the model. */
|
||||
parts: PromptPart['type'][]
|
||||
/** The actions that the agent can take. */
|
||||
actions: AgentAction['_type'][]
|
||||
}
|
||||
| {
|
||||
/** Whether the agent is active in this mode and can take actions. */
|
||||
active: false
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
* All agent mode definitions.
|
||||
*
|
||||
* To add a new mode, add a new object to this array.
|
||||
* To change what the agent can see, edit the `parts` array.
|
||||
* To change what the agent can do, edit the `actions` array.
|
||||
*
|
||||
* By importing utils directly, we ensure they are registered when this module is loaded.
|
||||
*/
|
||||
export const AGENT_MODE_DEFINITIONS = [
|
||||
{
|
||||
type: 'idling',
|
||||
active: false,
|
||||
},
|
||||
{
|
||||
type: 'working',
|
||||
active: true,
|
||||
|
||||
/**
|
||||
* Prompt parts determine what information will be sent to the model.
|
||||
*/
|
||||
parts: [
|
||||
// Mode (sends metadata to worker)
|
||||
ModePartUtil.type,
|
||||
|
||||
// Debug (sends debug flags to worker)
|
||||
DebugPartUtil.type,
|
||||
|
||||
// Model
|
||||
ModelNamePartUtil.type,
|
||||
|
||||
// Request
|
||||
MessagesPartUtil.type,
|
||||
DataPartUtil.type,
|
||||
ContextItemsPartUtil.type,
|
||||
|
||||
// Viewport
|
||||
ScreenshotPartUtil.type,
|
||||
UserViewportBoundsPartUtil.type,
|
||||
AgentViewportBoundsPartUtil.type,
|
||||
|
||||
// Shapes
|
||||
BlurryShapesPartUtil.type,
|
||||
PeripheralShapesPartUtil.type,
|
||||
SelectedShapesPartUtil.type,
|
||||
|
||||
// History
|
||||
ChatHistoryPartUtil.type,
|
||||
UserActionHistoryPartUtil.type,
|
||||
TodoListPartUtil.type,
|
||||
|
||||
// Lints
|
||||
CanvasLintsPartUtil.type,
|
||||
|
||||
// Metadata
|
||||
TimePartUtil.type,
|
||||
],
|
||||
|
||||
/**
|
||||
* Agent actions determine what actions the agent can take.
|
||||
*/
|
||||
actions: [
|
||||
// Communication
|
||||
MessageActionUtil.type,
|
||||
|
||||
// Planning
|
||||
ThinkActionUtil.type,
|
||||
ReviewActionUtil.type,
|
||||
AddDetailActionUtil.type,
|
||||
UpsertTodoListItemActionUtil.type,
|
||||
SetMyViewActionUtil.type,
|
||||
|
||||
// Individual shapes
|
||||
CreateActionUtil.type,
|
||||
DeleteActionUtil.type,
|
||||
UpdateActionUtil.type,
|
||||
LabelActionUtil.type,
|
||||
MoveActionUtil.type,
|
||||
|
||||
// Groups of shapes
|
||||
PlaceActionUtil.type,
|
||||
BringToFrontActionUtil.type,
|
||||
SendToBackActionUtil.type,
|
||||
RotateActionUtil.type,
|
||||
ResizeActionUtil.type,
|
||||
AlignActionUtil.type,
|
||||
DistributeActionUtil.type,
|
||||
StackActionUtil.type,
|
||||
ClearActionUtil.type,
|
||||
|
||||
// Drawing
|
||||
PenActionUtil.type,
|
||||
|
||||
// External APIs
|
||||
CountryInfoActionUtil.type,
|
||||
CountShapesActionUtil.type,
|
||||
|
||||
// Internal (required)
|
||||
UnknownActionUtil.type,
|
||||
],
|
||||
},
|
||||
] as const satisfies AgentModeDefinition[]
|
||||
|
||||
/**
|
||||
* An agent mode definition from the AGENT_MODE_DEFINITIONS array.
|
||||
*/
|
||||
export type AgentModeDefinitionType = (typeof AGENT_MODE_DEFINITIONS)[number]
|
||||
|
||||
/**
|
||||
* The type of an agent mode.
|
||||
*/
|
||||
export type AgentModeType = AgentModeDefinitionType['type']
|
||||
|
||||
/**
|
||||
* Get the mode definition for a given mode type.
|
||||
* @param type - The mode type to look up.
|
||||
* @returns The mode definition.
|
||||
*/
|
||||
export function getAgentModeDefinition(type: AgentModeType): AgentModeDefinitionType {
|
||||
const mode = AGENT_MODE_DEFINITIONS.find((m) => m.type === type)
|
||||
if (!mode) throw new Error(`Unknown agent mode: ${type}`)
|
||||
return mode
|
||||
}
|
||||
Reference in New Issue
Block a user