openwork

repository·main·Indexed 23 days ago

https://github.com/langchain-ai/openwork

A tactical desktop interface for deepagentsjs that provides a visual environment for building deep agents. It features filesystem access via LocalSandbox, planning capabilities, subagent delegation, and support for models from Anthropic, OpenAI, and Google. The system includes a SQLite-based checkpointing mechanism using SqlJsSaver for state persistence and thread management.

Tokens
7K
Snippets
17
Records
38
Agent score
81%

What's inside openwork

  1. Build and run openwork from source

    main

    If you want to run the development version from the local repository, follow these steps:

    1. Clone the repository.
    2. Navigate into the directory.
    3. Install dependencies.
    4. Run the development script.

    Note: Requires Node.js 18+.

    git clone https://github.com/langchain-ai/openwork.git
    cd openwork
    npm install
    npm run dev
  2. Agent filesystem and path handling rules

    main

    When using the createAgentRuntime, the agent operates in a LocalSandbox with virtualMode: false. This means the agent interacts with your actual filesystem using fully qualified absolute system paths.

    Key Pathing Rules:

    • The workspace root is defined by the workspacePath provided during runtime creation.
    • All file operations (e.g., ls, read_file, write_file, edit_file, glob, grep) must use absolute paths.
    • Example: If workspacePath is /Users/dev/project, a file in src should be referenced as /Users/dev/project/src/index.ts.
    • To list the root directory, the agent uses ls("/Users/dev/project").
  3. Use SqlJsSaver for SQLite checkpoint persistence

    main

    The SqlJsSaver class is a BaseCheckpointSaver implementation that uses sql.js to persist agent checkpoints in a SQLite database. Because it uses a pure JavaScript implementation of SQLite, it is compatible with environments like Electron without requiring native module compilation.

    Key Features:

    • In-memory with disk persistence: The database operates in memory for speed and is periodically (debounced) or manually flushed to a file on disk.
    • Automatic Backup: If the existing database file exceeds 100MB, SqlJsSaver will automatically back up the old file (with a .bak.[timestamp] suffix) and create a fresh database to prevent memory exhaustion.
    • Thread Management: Supports managing multiple threads via thread_id and checkpoint_ns (namespace).
  4. Understand custom event types in ElectronIPCTransport

    main

    Beyond standard message events, ElectronIPCTransport emits several custom event types to provide rich UI context:

    • type: "subagents": Emits an array of Subagent objects, tracking the status (running | completed), name, and description of active sub-tasks.
    • type: "workspace": Emits file system metadata including files (list of paths, is_dir, and size) and the path of the current workspace.
    • type: "interrupt": Emits when the graph hits a breakpoint requiring human intervention. It includes a request object with a tool_call and allowed_decisions (e.g., ['approve', 'reject', 'edit']).
    • type: "tool_call": Emits streaming tool call chunks or completed tool calls.
    • type: "token_usage": Emits usage metadata including inputTokens, outputTokens, totalTokens, and cache details.
  5. Implement Human-In-The-Loop (HITL) decisions

    main

    When a stream emits an interrupt event, it provides a HITLRequest. The user must respond with a HITLDecision to resolve the interruption.

    HITLRequest structure:

    • id: Unique request ID.
    • tool_call: The ToolCall that triggered the interruption.
    • allowed_decisions: An array of allowed decision types (approve, reject, or edit).

    HITLDecision structure:

    • type: One of approve, reject, or edit.
    • tool_call_id: The ID of the tool call being decided upon.
    • edited_args: (Optional) If the type is edit, provide the new arguments here.
    • feedback: (Optional) Textual feedback for the model.
  6. Structure of IPC messages and events

    main

    The openwork project uses an Inter-Process Communication (IPC) system to communicate between the main process and the client. Events are categorized by their type and follow specific schemas depending on whether they are delivering values, tokens, tool calls, or stream chunks.

    Message Types

    When receiving IPCMessage objects (often found within IPCValuesEvent), the type field indicates the origin of the content:

    • human: User input.
    • ai: Model response.
    • tool: Output from a tool execution.
    • system: System-level instructions or messages.

    Event Types

    All IPC events conform to the IPCEvent union type:

    • values: Contains the current state of the workspace, including messages, todos, files, workspacePath, subagents, and potential interrupt states.
    • token: Delivers individual string tokens for real-time text streaming, associated with a messageId.
    • tool_call: Signals that a tool is being invoked, providing tool_calls with id, name, and args.
    • stream: Forwards raw LangGraph stream chunks. The mode can be messages or values.
    • done: Indicates the process has completed successfully.
    • error: Indicates a failure, providing an error string.
    export type IPCEvent =
      | IPCValuesEvent
      | IPCTokenEvent
      | IPCToolCallEvent
      | IPCStreamEvent
      | IPCDoneEvent
      | IPCErrorEvent
  7. Handle Agent Stream Events

    main

    The agent emits a stream of events of type StreamEvent. You should implement a handler to process these different event types:

    • message: Contains a Message object.
    • tool_call: Contains a ToolCall object.
    • tool_result: Contains a ToolResult object.
    • interrupt: Contains a HITLRequest (Human-in-the-loop request).
    • token: Contains a single string token (for streaming text).
    • todos: Contains a list of Todo items.
    • workspace: Contains FileInfo[] and the current path.
    • subagents: Contains a list of Subagent statuses.
    • done: Contains the final result.
    • error: Contains an error string.
    export type StreamEvent =
      | { type: "message"; message: Message }
      | { type: "tool_call"; toolCall: ToolCall }
      | { type: "tool_result"; toolResult: ToolResult }
      | { type: "interrupt"; request: HITLRequest }
      | { type: "token"; token: string }
      | { type: "todos"; todos: Todo[] }
      | { type: "workspace"; files: FileInfo[]; path: string }
      | { type: "subagents"; subagents: Subagent[] }
      | { type: "done"; result: unknown }
      | { type: "error"; error: string }
  8. Configure LocalSandbox options

    main

    The LocalSandbox class provides filesystem and shell access to agents. You can configure its behavior using the LocalSandboxOptions object during instantiation.

    Configuration Options

    OptionTypeDefaultDescription
    rootDirstringprocess.cwd()The root directory for file operations and command execution.
    virtualModebooleanfalseIf enabled, / maps to the rootDir.
    maxFileSizeMbnumber10Maximum file size in MB for file operations.
    timeoutnumber120000 (2 mins)Command timeout in milliseconds.
    maxOutputBytesnumber100000 (~100KB)Maximum output bytes before truncation.
    envRecord<string, string>process.envEnvironment variables to pass to commands.

    Security Warning: LocalSandbox has no built-in safeguards. It is highly recommended to use human-in-the-loop (HITL) middleware to approve all command executions.

    const sandbox = new LocalSandbox({
      rootDir: '/path/to/workspace',
      virtualMode: true,
      timeout: 60_000,
    });
  9. Supported AI Models in openwork

    main

    openwork supports various models from Anthropic, OpenAI, and Google. You can configure these models within the application via the settings panel.

    | Provider  | Models                                                                                 |
    | --------- | -------------------------------------------------------------------------------------- |
    | Anthropic | Claude Opus 4.5, Claude Sonnet 4.5, Claude Haiku 4.5, Claude Opus 4.1, Claude Sonnet 4 |
    | OpenAI    | GPT-5.2, GPT-5.1, o3, o3 Mini, o4 Mini, o1, GPT-4.1, GPT-4o                            |
    | Google    | Gemini 3 Pro Preview, Gemini 3 Flash Preview, Gemini 2.5 Pro, Gemini 2.5 Flash, Gemini 2.5 Flash Lite |
  10. Execute shell commands with LocalSandbox.execute()

    main

    The execute(command: string) method runs a shell command within the sandbox's working directory. It supports both Windows (cmd.exe) and Unix-like (/bin/sh) environments.

    Return Value

    The method returns a Promise<ExecuteResponse> containing:

    • output: A string containing the combined stdout and stderr (with [stderr] prefixes for error lines). If the output exceeds maxOutputBytes, it is truncated.
    • exitCode: The exit code of the process, or null if the command timed out or was killed by a signal.
    • truncated: A boolean indicating if the output was truncated due to size limits.

    Example

    const result = await sandbox.execute('echo "Hello World"');
    // result.output: "Hello World\n"
    // result.exitCode: 0
    // result.truncated: false
    const result = await sandbox.execute('npm test');
    console.log(result.output);
    console.log('Exit code:', result.exitCode);