npm package: @futurework/agentic-work
Description: Personal AI Assistant - Agentic Work Platform
Latest version: 0.2.0

# 🤖 Agentic Work

**个人 AI 助手平台** — 本地 Agent，通过 WebSocket 连接上游 Gateway

## 架构

```
┌───────────────────────┐
│   Agentic Work        │
│   (TypeScript Worker) │
└───────────┬───────────┘
            │ WebSocket
            ▼
┌───────────────────────┐
│  future-of-work       │
│  Go API Gateway       │
│  ws://localhost/api   │
└───────────────────────┘
            │
            ▼ (前端 WebSocket)
┌───────────────────────┐
│  Web UI               │
│  (future-of-work)     │
└───────────────────────┘
```

## 特性

- **WebSocket 长连接** — 与上游 Gateway 保持实时双向通信
- **Steering / Follow-up** — 执行中打断当前计划，或完成后追加任务
- **实时工具进度** — 长时间运行的工具（shell 命令等）流式推送执行状态
- **上下文压缩** — 超过 token 阈值时自动压缩历史消息，对话不中断
- **弹性重试** — LLM 调用失败时指数退避重试，Rate Limit 超时可见失败
- **本地优先** — 配置数据存储在 `~/.agentic-work/`

## 快速开始

```bash
# 安装
pnpm add -g agentic-work

# 连接 Gateway
agentic gateway connect

# 指定地址
agentic gateway connect --host localhost --port 80
```

## 从源码构建

```bash
git clone https://github.com/yourname/agentic-work.git
cd agentic-work
pnpm install
pnpm build
```

## 配置

配置文件位于 `~/.agentic-work/config.json`：

```json
{
  "gateway": {
    "host": "localhost",
    "port": 80,
    "clientId": "agentic_xxx"
  },
  "ai": {
    "provider": "anthropic",
    "model": "claude-sonnet-4-20250514",
    "thinkingLevel": "on"
  },
  "credentials": {
    "anthropic": "sk-ant-..."
  }
}
```

## WebSocket 协议

Worker 与前端（通过 Gateway 中转）之间的消息协议：

### 前端 → Worker

| 消息类型 | Data | 说明 |
|---------|------|------|
| `agent.chat` | `{ message, session_key?, new_session?, stream?, images?, sender? }` | 发送消息 |
| `agent.abort` | — | 中止当前请求 |
| `agent.steer` | `{ message }` | **Steering**：实时打断当前计划（LLM streaming 期间立即生效） |
| `agent.follow_up` | `{ message }` | **Follow-up**：完成后追加任务 |
| `agent.state` | — | 查询 Agent 当前状态 |
| `config.get` | — | 获取配置 |
| `config.update` | `{ ai?, credentials? }` | 更新配置 |
| `sessions.list` | — | 会话列表 |
| `sessions.get` | `{ session_id }` | 获取会话消息 |
| `sessions.create` | `{ title? }` | 创建会话 |
| `sessions.delete` | `{ session_id }` | 删除会话 |
| `skills.list` | — | Skill 列表 |
| `skills.get` | `{ folder }` | Skill 详情 |
| `skills.create` | `{ name }` | 创建 Skill |
| `skills.save` | `{ folder, name, description, instructions? }` | 保存 Skill |
| `skills.toggle` | `{ folder, enabled }` | 启用/禁用 Skill |
| `skills.delete` | `{ folder }` | 删除 Skill |

### Worker → 前端

| 消息类型 | Data | 说明 |
|---------|------|------|
| `agent.stream.start` | — | 流式生成开始 |
| `agent.stream.delta` | `AgentEvent` | 流式事件（见下表） |
| `agent.reply` | `{ message, session_key, is_new_session, aborted, streamed }` | 最终回复 |
| `agent.abort.reply` | `{ success }` | Abort 结果 |
| `agent.steer.reply` | `{ success }` | Steering 注入结果 |
| `agent.follow_up.reply` | `{ success }` | Follow-up 注入结果 |
| `agent.state.reply` | `AgentState` | 状态查询响应 |
| `agent.state.changed` | `AgentState` | 状态变化推送（streaming 开始/结束时自动发送） |
| `state.changed` | `RuntimeEvent` | 运行时事件（workspace 变更、fs 变更、task 更新） |

### `agent.stream.delta` 事件类型

所有流式事件通过 `agent.stream.delta` 消息的 `data` 字段传递：

```typescript
type AgentEvent =
  | { type: "text_delta";        delta: string }
  | { type: "tool_start";        toolCallId: string; toolName: string; args: unknown }
  | { type: "tool_update";       toolCallId: string; toolName: string; text: string }
  | { type: "tool_done";         toolCallId: string; toolName: string; result: unknown; isError: boolean }
  | { type: "tool_skipped";      toolCallId: string; toolName: string }
  | { type: "turn_retry";        turn: number; attempt: number; error: string; waitMs: number }
  | { type: "steering_injected"; messages: string[] }
  | { type: "followup_injected"; messages: string[] }
  | { type: "error";             error: string; aborted: boolean }
  | { type: "agent_end";         fullText: string; toolCalls: ToolCallInfo[]; aborted: boolean }
```

### `AgentState` 结构

```typescript
interface AgentState {
  isStreaming: boolean;       // 是否正在生成
  steeringQueueSize: number;  // steering 队列中的消息数
  followUpQueueSize: number;  // follow-up 队列中的消息数
  currentSessionKey: string | null;
  contextMessageCount: number;
  totalTokens: { input: number; output: number; calls: number };
}
```

### Steering 使用示例

```javascript
// 前端：发送 Steering（LLM streaming 期间立即生效）
ws.send(JSON.stringify({
  type: "agent.steer",
  data: { message: "停！改为用 TypeScript 重写" }
}));

// 响应
ws.onmessage = ({ data }) => {
  const msg = JSON.parse(data);
  if (msg.type === "agent.stream.delta") {
    const event = msg.data;
    if (event.type === "steering_injected") {
      // UI 显示 "已打断，正在处理新指令..."
    }
    if (event.type === "tool_update") {
      // 显示工具执行进度：event.text = "Still running... (5s)"
    }
  }
};
```

## 开发

```bash
pnpm test         # 运行测试
pnpm lint         # 代码检查
npx tsc --noEmit  # 类型检查
```

## 技术栈

- **运行时**: Node.js 22+
- **语言**: TypeScript ESM
- **包管理**: pnpm
- **CLI**: Commander.js
- **WebSocket**: ws
- **AI SDK**: Vercel AI SDK
- **测试**: Vitest
- **Lint**: Oxlint

## 许可证

MIT License