@gird/ccli 2.18.0 → 2.19.0
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 +51 -2
- package/dist/agent/os.js +69 -0
- package/dist/agent/os.js.map +1 -0
- package/dist/cli/args/index.js +36 -0
- package/dist/cli/args/index.js.map +1 -1
- package/dist/cli/repl/index.js +1 -0
- package/dist/cli/repl/index.js.map +1 -1
- package/dist/cli/run/index.js +5 -2
- package/dist/cli/run/index.js.map +1 -1
- package/dist/cli/session/checkpoint.js +5 -4
- package/dist/cli/session/checkpoint.js.map +1 -1
- package/dist/cli/slash/config/model.js +30 -9
- package/dist/cli/slash/config/model.js.map +1 -1
- package/dist/config/config.js +21 -0
- package/dist/config/config.js.map +1 -1
- package/dist/harness/index.js +6 -0
- package/dist/harness/index.js.map +1 -1
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -1
- package/dist/server/index.js +338 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/sessions.js +181 -0
- package/dist/server/sessions.js.map +1 -0
- package/dist/server/webui.html +1341 -0
- package/dist/server/webui.js +12 -0
- package/dist/server/webui.js.map +1 -0
- package/dist/tools/bash-background.js +5 -6
- package/dist/tools/bash-background.js.map +1 -1
- package/dist/tools/bash.js +6 -7
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/edit-file.js +10 -4
- package/dist/tools/edit-file.js.map +1 -1
- package/dist/tools/glob.js +40 -6
- package/dist/tools/glob.js.map +1 -1
- package/dist/tools/grep.js +48 -8
- package/dist/tools/grep.js.map +1 -1
- package/dist/tools/list-dir.js +13 -2
- package/dist/tools/list-dir.js.map +1 -1
- package/dist/tools/read-file.js +4 -2
- package/dist/tools/read-file.js.map +1 -1
- package/dist/tools/shared.js.map +1 -1
- package/dist/tools/write-file.js +11 -4
- package/dist/tools/write-file.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 会话管理 —— 让 Server 支持「有状态多轮 + 常驻事件流」。
|
|
3
|
+
*
|
|
4
|
+
* 第 114 步的 `/prompt` 是无状态单次调用;本模块给每个会话维护一份**持久历史**与
|
|
5
|
+
* 一组 **SSE 订阅者**:一次 prompt 触发一个回合,产生的 HarnessEvent 广播给所有订阅
|
|
6
|
+
* 该会话 stream 的客户端,回合结束把助手文本回给发起方。
|
|
7
|
+
*
|
|
8
|
+
* 第 116 步新增「审批回路」:破坏性工具触发 confirm() 时,Server 生成 approvalId
|
|
9
|
+
* 通过 SSE 推给客户端,再阻塞等待客户端通过 POST /approvals 回执。
|
|
10
|
+
*
|
|
11
|
+
* backend 是**注入点**:默认用真实 Harness(每会话一个),测试可换成发假事件的实现,
|
|
12
|
+
* 从而无需真实 provider 就能验证会话/广播逻辑。
|
|
13
|
+
*/
|
|
14
|
+
import { randomUUID } from 'node:crypto';
|
|
15
|
+
import { createCcliHarness } from '../harness/index.js';
|
|
16
|
+
import { emptyStats } from '../cli/stats/index.js';
|
|
17
|
+
/** 默认后端:真实 Harness,每会话一个。 */
|
|
18
|
+
export const harnessBackend = async (noHooks) => {
|
|
19
|
+
const result = await createCcliHarness({ noHooks });
|
|
20
|
+
if (!result.ok)
|
|
21
|
+
return { ok: false, message: result.message };
|
|
22
|
+
const harness = result.harness;
|
|
23
|
+
const systemPrompt = harness.buildSystemPrompt(harness.roleBasePrompt('default'));
|
|
24
|
+
const stats = emptyStats();
|
|
25
|
+
const backend = {
|
|
26
|
+
async runTurn(history, prompt, confirm, emit) {
|
|
27
|
+
history.push({ role: 'user', content: prompt });
|
|
28
|
+
await harness.runTurnWithEvents({ history, confirm, stats, emit });
|
|
29
|
+
},
|
|
30
|
+
close: () => harness.close(),
|
|
31
|
+
};
|
|
32
|
+
return { ok: true, backend, systemPrompt };
|
|
33
|
+
};
|
|
34
|
+
export class SessionManager {
|
|
35
|
+
createBackend;
|
|
36
|
+
noHooks;
|
|
37
|
+
sessions = new Map();
|
|
38
|
+
constructor(createBackend = harnessBackend, noHooks) {
|
|
39
|
+
this.createBackend = createBackend;
|
|
40
|
+
this.noHooks = noHooks;
|
|
41
|
+
}
|
|
42
|
+
/** 新建会话:建后端、以 system prompt 起头历史。 */
|
|
43
|
+
async create() {
|
|
44
|
+
const made = await this.createBackend(this.noHooks);
|
|
45
|
+
if (!made.ok)
|
|
46
|
+
return made;
|
|
47
|
+
const id = randomUUID();
|
|
48
|
+
this.sessions.set(id, {
|
|
49
|
+
id,
|
|
50
|
+
backend: made.backend,
|
|
51
|
+
history: [{ role: 'system', content: made.systemPrompt }],
|
|
52
|
+
subscribers: new Set(),
|
|
53
|
+
busy: false,
|
|
54
|
+
pendingApprovals: new Map(),
|
|
55
|
+
});
|
|
56
|
+
return { ok: true, id };
|
|
57
|
+
}
|
|
58
|
+
has(id) {
|
|
59
|
+
return this.sessions.has(id);
|
|
60
|
+
}
|
|
61
|
+
list() {
|
|
62
|
+
return [...this.sessions.keys()];
|
|
63
|
+
}
|
|
64
|
+
/** 订阅会话事件流;返回退订函数。会话不存在返回 null。 */
|
|
65
|
+
subscribe(id, onEvent) {
|
|
66
|
+
const s = this.sessions.get(id);
|
|
67
|
+
if (!s)
|
|
68
|
+
return null;
|
|
69
|
+
s.subscribers.add(onEvent);
|
|
70
|
+
return () => s.subscribers.delete(onEvent);
|
|
71
|
+
}
|
|
72
|
+
/** 向一个会话的所有订阅者广播事件(Server 层用于发带 approvalId 的 permission_request)。 */
|
|
73
|
+
broadcast(id, event) {
|
|
74
|
+
const s = this.sessions.get(id);
|
|
75
|
+
if (!s)
|
|
76
|
+
return;
|
|
77
|
+
for (const sub of s.subscribers)
|
|
78
|
+
sub(event);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* 向会话发一个 prompt:跑一个回合,事件广播给订阅者,回合结束返回助手文本。
|
|
82
|
+
* 会话不存在 → 404;正忙 → 409。
|
|
83
|
+
*
|
|
84
|
+
* confirm 回调由调用方构造(Server 层负责阻塞式审批回路)。
|
|
85
|
+
* emitFilter 可选:在事件广播前拦截/修改事件(如抑制无 approvalId 的 permission_request)。
|
|
86
|
+
*/
|
|
87
|
+
async prompt(id, prompt, confirm, emitFilter) {
|
|
88
|
+
const s = this.sessions.get(id);
|
|
89
|
+
if (!s)
|
|
90
|
+
return { ok: false, status: 404, error: `会话不存在:${id}` };
|
|
91
|
+
if (s.busy) {
|
|
92
|
+
return { ok: false, status: 409, error: '会话正忙,请稍后再试' };
|
|
93
|
+
}
|
|
94
|
+
s.busy = true;
|
|
95
|
+
let text = '';
|
|
96
|
+
const emit = (event) => {
|
|
97
|
+
if (event.type === 'text_delta')
|
|
98
|
+
text += event.text;
|
|
99
|
+
// 如果有过滤器且返回 false,则不广播此事件
|
|
100
|
+
if (emitFilter && !emitFilter(event))
|
|
101
|
+
return;
|
|
102
|
+
for (const sub of s.subscribers)
|
|
103
|
+
sub(event);
|
|
104
|
+
};
|
|
105
|
+
try {
|
|
106
|
+
await s.backend.runTurn(s.history, prompt, confirm, emit);
|
|
107
|
+
return { ok: true, text };
|
|
108
|
+
}
|
|
109
|
+
catch (e) {
|
|
110
|
+
return {
|
|
111
|
+
ok: false,
|
|
112
|
+
status: 500,
|
|
113
|
+
error: e?.message ?? String(e),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
finally {
|
|
117
|
+
s.busy = false;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* 创建一个审批请求并阻塞等待客户端回执。
|
|
122
|
+
*
|
|
123
|
+
* 返回的 Promise 在客户端调用 `resolveApproval()` 或超时后兑现。
|
|
124
|
+
* 超时默认 5 分钟,自动拒绝并清理。
|
|
125
|
+
*/
|
|
126
|
+
requestApproval(id, approvalId, timeoutMs = 5 * 60 * 1000) {
|
|
127
|
+
const s = this.sessions.get(id);
|
|
128
|
+
if (!s)
|
|
129
|
+
return Promise.resolve('no');
|
|
130
|
+
return new Promise((resolve) => {
|
|
131
|
+
let timer;
|
|
132
|
+
const done = (choice) => {
|
|
133
|
+
if (timer)
|
|
134
|
+
clearTimeout(timer);
|
|
135
|
+
s.pendingApprovals.delete(approvalId);
|
|
136
|
+
resolve(choice);
|
|
137
|
+
};
|
|
138
|
+
s.pendingApprovals.set(approvalId, done);
|
|
139
|
+
// 超时自动拒绝
|
|
140
|
+
timer = setTimeout(() => {
|
|
141
|
+
if (s.pendingApprovals.has(approvalId)) {
|
|
142
|
+
done('no');
|
|
143
|
+
}
|
|
144
|
+
}, timeoutMs);
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* 回执一个审批请求,解除 `requestApproval()` 的阻塞。
|
|
149
|
+
*
|
|
150
|
+
* 返回 true 表示找到了对应的待审批;false 表示 approvalId 不存在或已超时。
|
|
151
|
+
*/
|
|
152
|
+
resolveApproval(id, approvalId, choice) {
|
|
153
|
+
const s = this.sessions.get(id);
|
|
154
|
+
if (!s)
|
|
155
|
+
return false;
|
|
156
|
+
const resolve = s.pendingApprovals.get(approvalId);
|
|
157
|
+
if (!resolve)
|
|
158
|
+
return false;
|
|
159
|
+
resolve(choice);
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
/** 关闭并移除一个会话。 */
|
|
163
|
+
async close(id) {
|
|
164
|
+
const s = this.sessions.get(id);
|
|
165
|
+
if (!s)
|
|
166
|
+
return false;
|
|
167
|
+
// 清理所有挂起的审批(自动拒绝)
|
|
168
|
+
for (const [, resolve] of s.pendingApprovals)
|
|
169
|
+
resolve('no');
|
|
170
|
+
s.pendingApprovals.clear();
|
|
171
|
+
this.sessions.delete(id);
|
|
172
|
+
await s.backend.close();
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
/** 关闭全部会话(server 停机时)。 */
|
|
176
|
+
async closeAll() {
|
|
177
|
+
for (const id of [...this.sessions.keys()])
|
|
178
|
+
await this.close(id);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=sessions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessions.js","sourceRoot":"","sources":["../../src/server/sessions.ts"],"names":[],"mappings":"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"}
|