workspacecord 1.1.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -0
- package/README.zh-CN.md +40 -0
- package/dist/{archive-manager-KMNAWQIN.js → archive-manager-XM3UPOY2.js} +5 -7
- package/dist/{attachment-cli-QEF7IZBI.js → attachment-cli-4TGLBBUO.js} +3 -3
- package/dist/{chunk-7KESUGJP.js → chunk-54DP53ZK.js} +2 -2
- package/dist/chunk-COXPTYH5.js +165 -0
- package/dist/chunk-FXOGIVVO.js +2249 -0
- package/dist/{chunk-PUKMHDXS.js → chunk-GMYN4SYT.js} +175 -9
- package/dist/{chunk-Q2AJ4UEK.js → chunk-LBHCSIOK.js} +2 -2
- package/dist/chunk-MO4EEYFW.js +38 -0
- package/dist/chunk-QNMCFWNT.js +13280 -0
- package/dist/chunk-QWPKAUSV.js +18177 -0
- package/dist/chunk-RK6EIZOL.js +589 -0
- package/dist/chunk-UEX7U2KW.js +6528 -0
- package/dist/cli-framework-V2GGW6TO.js +18 -0
- package/dist/cli.js +15 -11
- package/dist/{codex-launcher-OOF2WYQF.js → codex-launcher-VDQ5VZPT.js} +3 -3
- package/dist/{codex-provider-P7TDYK6B.js → codex-provider-NYI7KBGO.js} +62 -24
- package/dist/{config-cli-YSKOXADF.js → config-cli-RQR2ZRQ5.js} +3 -3
- package/dist/{daemon-NW4WRMQK.js → daemon-JGPVKLK3.js} +1 -1
- package/dist/panel-adapter-DAFBW5CV.js +46 -0
- package/dist/{project-cli-QW5QYT6A.js → project-cli-6P6ZWDR6.js} +3 -3
- package/dist/{project-registry-LL75XEUV.js → project-registry-OEVPECMS.js} +3 -3
- package/dist/sdk-V7A7IF7F.js +43 -0
- package/dist/session-local-registration-RIO5EPZ5.js +14 -0
- package/dist/{setup-QJ4HVVCU.js → setup-KOS7SRSL.js} +3 -3
- package/package.json +10 -38
- package/dist/chunk-52OOARML.js +0 -284
- package/dist/chunk-CBNENUW6.js +0 -55
- package/dist/chunk-D6J3X35H.js +0 -96
- package/dist/chunk-G6CCOBUY.js +0 -7310
- package/dist/chunk-IFSOU4XI.js +0 -1477
- package/dist/chunk-K3NQKI34.js +0 -10
- package/dist/cli-framework-KMQXM2PO.js +0 -17
- package/dist/thread-manager-DRWNFXXY.js +0 -88
package/README.md
CHANGED
|
@@ -35,7 +35,11 @@ Discord Server
|
|
|
35
35
|
- Discord-side project binding with `/project setup`
|
|
36
36
|
- Main session channels and subagent threads
|
|
37
37
|
- Session archiving into `#history`
|
|
38
|
+
- Project-level personality, reusable skills, and MCP registry management
|
|
38
39
|
- Support for both Claude and Codex providers
|
|
40
|
+
- `monitor` mode with continued steering until task completion
|
|
41
|
+
- Remote human approval / gate handling from Discord for managed sessions
|
|
42
|
+
- Managed local `Codex` launch plus automatic local session discovery/sync
|
|
39
43
|
- Global config storage without requiring a project-local `.env`
|
|
40
44
|
- Optional daemon install and background management
|
|
41
45
|
|
|
@@ -153,6 +157,42 @@ workspacecord codex [options] # Launch managed Codex session with remote appro
|
|
|
153
157
|
- `/subagent list` — list subagents for the current session
|
|
154
158
|
- `/shell run` / `/shell processes` / `/shell kill`
|
|
155
159
|
|
|
160
|
+
|
|
161
|
+
## Capability Matrix
|
|
162
|
+
|
|
163
|
+
| Capability | Status | Entry Points |
|
|
164
|
+
|---|---|---|
|
|
165
|
+
| Global configuration | Stable | `workspacecord config *` |
|
|
166
|
+
| Explicit local project mounting | Stable | `workspacecord project *` |
|
|
167
|
+
| Discord category binding | Stable | `/project setup`, `/project info` |
|
|
168
|
+
| Project-level personality | Available | `/project personality`, `/project personality-clear` |
|
|
169
|
+
| Project-level reusable skills | Available | `/project skill-add`, `/project skill-list`, `/project skill-run` |
|
|
170
|
+
| Project-level MCP registry | Available | `/project mcp-add`, `/project mcp-list`, `/project mcp-remove` |
|
|
171
|
+
| Main agent sessions | Stable | `/agent spawn`, `/agent list`, `/agent archive`, `/agent cleanup` |
|
|
172
|
+
| Subagent threads | Stable | `/subagent run`, `/subagent list` |
|
|
173
|
+
| Session execution modes | Stable | `/agent mode`, `/agent goal`, `/agent verbose` |
|
|
174
|
+
| Remote human approval / gates | Available | interaction cards, `monitor` mode, Claude permission handling |
|
|
175
|
+
| Managed local Codex launch | Available | `workspacecord codex` |
|
|
176
|
+
| Local session discovery / sync | Available | hook / codex-log / sync recovery paths |
|
|
177
|
+
| Shell execution from Discord | Available | `/shell run`, `/shell processes`, `/shell kill` |
|
|
178
|
+
| History archive | Stable | `#history`, `/agent archive` |
|
|
179
|
+
| Daemon installation / background run | Available | `workspacecord daemon *` |
|
|
180
|
+
|
|
181
|
+
## Runtime Architecture
|
|
182
|
+
|
|
183
|
+
Current main runtime path:
|
|
184
|
+
|
|
185
|
+
```text
|
|
186
|
+
bot.ts
|
|
187
|
+
-> BotEventRouter
|
|
188
|
+
-> command handlers / button handler / message handler
|
|
189
|
+
-> thread-manager façade
|
|
190
|
+
-> session-registry / session runtime / state machine / panel adapter
|
|
191
|
+
-> Discord delivery + status cards + summaries
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
This repository intentionally keeps Discord as the control plane, while local project state, provider sessions, approvals, and output rendering stay on the machine that runs `workspacecord`.
|
|
195
|
+
|
|
156
196
|
## Development
|
|
157
197
|
|
|
158
198
|
Run the standard verification flow:
|
package/README.zh-CN.md
CHANGED
|
@@ -35,7 +35,11 @@ Discord Server
|
|
|
35
35
|
- 使用 `/project setup` 在 Discord 中绑定项目
|
|
36
36
|
- 主会话频道与子代理线程模型
|
|
37
37
|
- 会话自动归档到 `#history`
|
|
38
|
+
- 项目级人格、可复用技能和 MCP 服务注册管理
|
|
38
39
|
- 同时支持 Claude 与 Codex 提供方
|
|
40
|
+
- `monitor` 模式可持续推进直到任务完成
|
|
41
|
+
- 托管会话支持 Discord 远程人工审批 / 门控处理
|
|
42
|
+
- 支持本地托管 `Codex` 启动与自动会话发现 / 同步
|
|
39
43
|
- 使用全局配置存储,不依赖项目内 `.env`
|
|
40
44
|
- 支持守护进程安装与后台管理
|
|
41
45
|
|
|
@@ -153,6 +157,42 @@ workspacecord codex [options] # 启动托管的 Codex 会话(支持远程审
|
|
|
153
157
|
- `/subagent list`:查看当前会话的子代理
|
|
154
158
|
- `/shell run` / `/shell processes` / `/shell kill`
|
|
155
159
|
|
|
160
|
+
|
|
161
|
+
## 能力矩阵
|
|
162
|
+
|
|
163
|
+
| 能力 | 状态 | 入口 |
|
|
164
|
+
|---|---|---|
|
|
165
|
+
| 全局配置 | 稳定 | `workspacecord config *` |
|
|
166
|
+
| 显式本地项目挂载 | 稳定 | `workspacecord project *` |
|
|
167
|
+
| Discord 分类绑定 | 稳定 | `/project setup`, `/project info` |
|
|
168
|
+
| 项目级人格 | 可用 | `/project personality`, `/project personality-clear` |
|
|
169
|
+
| 项目级可复用技能 | 可用 | `/project skill-add`, `/project skill-list`, `/project skill-run` |
|
|
170
|
+
| 项目级 MCP 注册表 | 可用 | `/project mcp-add`, `/project mcp-list`, `/project mcp-remove` |
|
|
171
|
+
| 主代理会话 | 稳定 | `/agent spawn`, `/agent list`, `/agent archive`, `/agent cleanup` |
|
|
172
|
+
| 子代理线程 | 稳定 | `/subagent run`, `/subagent list` |
|
|
173
|
+
| 会话执行模式 | 稳定 | `/agent mode`, `/agent goal`, `/agent verbose` |
|
|
174
|
+
| 远程人工审批 / 门控 | 可用 | 交互卡、`monitor` 模式、Claude 权限处理 |
|
|
175
|
+
| 本地托管 Codex 启动 | 可用 | `workspacecord codex` |
|
|
176
|
+
| 本地会话发现 / 同步 | 可用 | hook / codex-log / sync 恢复路径 |
|
|
177
|
+
| 从 Discord 执行 shell | 可用 | `/shell run`, `/shell processes`, `/shell kill` |
|
|
178
|
+
| 历史归档 | 稳定 | `#history`, `/agent archive` |
|
|
179
|
+
| 守护进程安装 / 后台运行 | 可用 | `workspacecord daemon *` |
|
|
180
|
+
|
|
181
|
+
## 运行时架构
|
|
182
|
+
|
|
183
|
+
当前主运行路径:
|
|
184
|
+
|
|
185
|
+
```text
|
|
186
|
+
bot.ts
|
|
187
|
+
-> BotEventRouter
|
|
188
|
+
-> command handlers / button handler / message handler
|
|
189
|
+
-> thread-manager façade
|
|
190
|
+
-> session-registry / session runtime / state machine / panel adapter
|
|
191
|
+
-> Discord delivery + status cards + summaries
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
这意味着 Discord 只是控制平面;真正的项目状态、provider 会话、审批、输出渲染都发生在运行 `workspacecord` 的本机上。
|
|
195
|
+
|
|
156
196
|
## 开发与验证
|
|
157
197
|
|
|
158
198
|
建议先运行基础验证:
|
|
@@ -5,13 +5,11 @@ import {
|
|
|
5
5
|
getArchivedSessions,
|
|
6
6
|
isArchivedProviderSession,
|
|
7
7
|
loadArchived
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import "./chunk-
|
|
10
|
-
import "./chunk-
|
|
11
|
-
import "./chunk-
|
|
12
|
-
import "./chunk-
|
|
13
|
-
import "./chunk-52OOARML.js";
|
|
14
|
-
import "./chunk-K3NQKI34.js";
|
|
8
|
+
} from "./chunk-GMYN4SYT.js";
|
|
9
|
+
import "./chunk-54DP53ZK.js";
|
|
10
|
+
import "./chunk-RK6EIZOL.js";
|
|
11
|
+
import "./chunk-UEX7U2KW.js";
|
|
12
|
+
import "./chunk-MO4EEYFW.js";
|
|
15
13
|
export {
|
|
16
14
|
archiveSession,
|
|
17
15
|
checkAutoArchive,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
fetchRegisteredAttachments
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
6
|
-
import "./chunk-
|
|
4
|
+
} from "./chunk-LBHCSIOK.js";
|
|
5
|
+
import "./chunk-UEX7U2KW.js";
|
|
6
|
+
import "./chunk-MO4EEYFW.js";
|
|
7
7
|
|
|
8
8
|
// src/attachment-cli.ts
|
|
9
9
|
function printHelp() {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
Store
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-UEX7U2KW.js";
|
|
5
5
|
|
|
6
|
-
// src/project-registry.ts
|
|
6
|
+
// ../engine/src/project-registry.ts
|
|
7
7
|
import { randomUUID } from "crypto";
|
|
8
8
|
import { resolve } from "path";
|
|
9
9
|
var store = new Store("projects.json");
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
createSession,
|
|
4
|
+
getSessionByProviderSession,
|
|
5
|
+
updateSession
|
|
6
|
+
} from "./chunk-RK6EIZOL.js";
|
|
7
|
+
import {
|
|
8
|
+
resolvePath
|
|
9
|
+
} from "./chunk-UEX7U2KW.js";
|
|
10
|
+
|
|
11
|
+
// ../bot/src/session-local-registration.ts
|
|
12
|
+
import { sep } from "path";
|
|
13
|
+
function buildClaudeSubagentProviderSessionId(parentProviderSessionId, agentId) {
|
|
14
|
+
return `subagent:${parentProviderSessionId}:${agentId}`;
|
|
15
|
+
}
|
|
16
|
+
function updateLocalObservation(sessionId, patch) {
|
|
17
|
+
updateSession(sessionId, {
|
|
18
|
+
discoverySource: patch.discoverySource,
|
|
19
|
+
lastObservedAt: Date.now(),
|
|
20
|
+
lastObservedCwd: resolvePath(patch.cwd),
|
|
21
|
+
...patch.remoteHumanControl !== void 0 ? { remoteHumanControl: patch.remoteHumanControl } : {}
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
async function registerLocalSession(params, guild) {
|
|
25
|
+
const {
|
|
26
|
+
provider,
|
|
27
|
+
providerSessionId,
|
|
28
|
+
cwd,
|
|
29
|
+
discoverySource,
|
|
30
|
+
labelHint,
|
|
31
|
+
remoteHumanControl,
|
|
32
|
+
subagent
|
|
33
|
+
} = params;
|
|
34
|
+
const effectiveProviderSessionId = provider === "claude" && subagent?.parentProviderSessionId && subagent.agentId ? buildClaudeSubagentProviderSessionId(subagent.parentProviderSessionId, subagent.agentId) : providerSessionId;
|
|
35
|
+
const effectiveAgentLabel = subagent?.agentType || labelHint || effectiveProviderSessionId.slice(0, 12);
|
|
36
|
+
const { isArchivedProviderSession } = await import("./archive-manager-XM3UPOY2.js");
|
|
37
|
+
if (isArchivedProviderSession(provider, effectiveProviderSessionId)) {
|
|
38
|
+
console.log(
|
|
39
|
+
`[registerLocalSession] Skip archived ${provider} session ${effectiveProviderSessionId} (source: ${discoverySource})`
|
|
40
|
+
);
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const existing = getSessionByProviderSession(provider, effectiveProviderSessionId);
|
|
44
|
+
if (existing) {
|
|
45
|
+
updateLocalObservation(existing.id, { discoverySource, cwd, remoteHumanControl });
|
|
46
|
+
return { session: existing, isNewlyCreated: false };
|
|
47
|
+
}
|
|
48
|
+
const { getProjectByPath, getAllRegisteredProjects } = await import("./project-registry-OEVPECMS.js");
|
|
49
|
+
const { ChannelType, ThreadAutoArchiveDuration } = await import("discord.js");
|
|
50
|
+
const normalizedCwd = resolvePath(cwd);
|
|
51
|
+
let project = getProjectByPath(normalizedCwd);
|
|
52
|
+
if (!project) {
|
|
53
|
+
const allProjects = getAllRegisteredProjects();
|
|
54
|
+
let bestMatch;
|
|
55
|
+
let bestMatchPathLength = -1;
|
|
56
|
+
for (const p of allProjects) {
|
|
57
|
+
const projectPath = resolvePath(p.path);
|
|
58
|
+
if (normalizedCwd.startsWith(projectPath + sep) && projectPath.length > bestMatchPathLength) {
|
|
59
|
+
bestMatch = p;
|
|
60
|
+
bestMatchPathLength = projectPath.length;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
project = bestMatch;
|
|
64
|
+
}
|
|
65
|
+
if (!project || !project.discordCategoryId) {
|
|
66
|
+
console.warn(
|
|
67
|
+
`[registerLocalSession] Cannot register ${provider} session ${providerSessionId}: cwd "${cwd}" does not belong to any mounted project`
|
|
68
|
+
);
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
const category = guild.channels.cache.get(project.discordCategoryId);
|
|
72
|
+
if (!category || category.type !== ChannelType.GuildCategory) {
|
|
73
|
+
console.warn(
|
|
74
|
+
`[registerLocalSession] Cannot register ${provider} session ${providerSessionId}: category ${project.discordCategoryId} not found`
|
|
75
|
+
);
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
if (subagent?.parentProviderSessionId) {
|
|
79
|
+
const parentSession = getSessionByProviderSession(provider, subagent.parentProviderSessionId);
|
|
80
|
+
if (!parentSession) {
|
|
81
|
+
console.warn(
|
|
82
|
+
`[registerLocalSession] Delaying subagent ${provider} session ${providerSessionId}: parent provider session ${subagent.parentProviderSessionId} not registered yet`
|
|
83
|
+
);
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
const parentChannel = guild.channels.cache.get(parentSession.channelId);
|
|
87
|
+
const threadHostChannel = parentChannel?.type === ChannelType.GuildText ? parentChannel : parentChannel?.isThread?.() || parentChannel?.type === ChannelType.PublicThread ? parentChannel.parent : void 0;
|
|
88
|
+
if (threadHostChannel?.type !== ChannelType.GuildText) {
|
|
89
|
+
console.warn(
|
|
90
|
+
`[registerLocalSession] Delaying subagent ${provider} session ${providerSessionId}: parent channel ${parentSession.channelId} is unavailable`
|
|
91
|
+
);
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
const normalizedThreadName = `[sub:${provider}] ${effectiveAgentLabel}`.slice(0, 100);
|
|
95
|
+
const thread = await threadHostChannel.threads.create({
|
|
96
|
+
name: normalizedThreadName,
|
|
97
|
+
type: ChannelType.PublicThread,
|
|
98
|
+
autoArchiveDuration: ThreadAutoArchiveDuration.OneHour,
|
|
99
|
+
reason: `Auto-registered subagent session ${effectiveProviderSessionId}`
|
|
100
|
+
});
|
|
101
|
+
const session2 = await createSession({
|
|
102
|
+
channelId: thread.id,
|
|
103
|
+
categoryId: parentSession.categoryId,
|
|
104
|
+
projectName: parentSession.projectName,
|
|
105
|
+
agentLabel: effectiveAgentLabel,
|
|
106
|
+
provider,
|
|
107
|
+
providerSessionId: effectiveProviderSessionId,
|
|
108
|
+
directory: normalizedCwd,
|
|
109
|
+
type: "subagent",
|
|
110
|
+
parentChannelId: parentSession.type === "subagent" ? parentSession.parentChannelId ?? threadHostChannel.id : parentSession.channelId,
|
|
111
|
+
subagentDepth: Math.max(1, subagent.depth ?? parentSession.subagentDepth + 1),
|
|
112
|
+
discoverySource,
|
|
113
|
+
remoteHumanControl: remoteHumanControl ?? false
|
|
114
|
+
});
|
|
115
|
+
updateLocalObservation(session2.id, {
|
|
116
|
+
discoverySource,
|
|
117
|
+
cwd: normalizedCwd,
|
|
118
|
+
remoteHumanControl: remoteHumanControl ?? false
|
|
119
|
+
});
|
|
120
|
+
console.log(
|
|
121
|
+
`[registerLocalSession] Registered subagent ${provider} session ${effectiveProviderSessionId} (source: ${discoverySource}, parent: ${parentSession.channelId}, thread: ${thread.id})`
|
|
122
|
+
);
|
|
123
|
+
return { session: session2, isNewlyCreated: true };
|
|
124
|
+
}
|
|
125
|
+
const base = labelHint ? labelHint.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 60) : effectiveProviderSessionId.slice(0, 12);
|
|
126
|
+
const channelName = `${provider}-${base}`.slice(0, 100);
|
|
127
|
+
let channel = category.children.cache.find(
|
|
128
|
+
(ch) => ch.type === ChannelType.GuildText && typeof ch.topic === "string" && ch.topic.includes(`Provider Session: ${effectiveProviderSessionId}`)
|
|
129
|
+
);
|
|
130
|
+
if (!channel) {
|
|
131
|
+
channel = await guild.channels.create({
|
|
132
|
+
name: channelName,
|
|
133
|
+
type: ChannelType.GuildText,
|
|
134
|
+
parent: category.id,
|
|
135
|
+
topic: `${provider} session (local) | Provider Session: ${effectiveProviderSessionId}`
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
const session = await createSession({
|
|
139
|
+
channelId: channel.id,
|
|
140
|
+
categoryId: project.discordCategoryId,
|
|
141
|
+
projectName: project.name,
|
|
142
|
+
agentLabel: effectiveAgentLabel,
|
|
143
|
+
provider,
|
|
144
|
+
providerSessionId: effectiveProviderSessionId,
|
|
145
|
+
directory: normalizedCwd,
|
|
146
|
+
type: "persistent",
|
|
147
|
+
discoverySource,
|
|
148
|
+
remoteHumanControl: remoteHumanControl ?? false
|
|
149
|
+
});
|
|
150
|
+
updateLocalObservation(session.id, {
|
|
151
|
+
discoverySource,
|
|
152
|
+
cwd: normalizedCwd,
|
|
153
|
+
remoteHumanControl: remoteHumanControl ?? false
|
|
154
|
+
});
|
|
155
|
+
console.log(
|
|
156
|
+
`[registerLocalSession] Registered ${provider} session ${effectiveProviderSessionId} (source: ${discoverySource}, channel: ${channel.id})`
|
|
157
|
+
);
|
|
158
|
+
return { session, isNewlyCreated: true };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export {
|
|
162
|
+
buildClaudeSubagentProviderSessionId,
|
|
163
|
+
updateLocalObservation,
|
|
164
|
+
registerLocalSession
|
|
165
|
+
};
|