@shareai-lab/kode-sdk 1.0.0-beta.8 → 2.7.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/LICENSE +21 -0
- package/README.md +115 -273
- package/README.zh-CN.md +114 -0
- package/dist/core/agent/breakpoint-manager.d.ts +16 -0
- package/dist/core/agent/breakpoint-manager.js +36 -0
- package/dist/core/agent/message-queue.d.ts +26 -0
- package/dist/core/agent/message-queue.js +57 -0
- package/dist/core/agent/permission-manager.d.ts +9 -0
- package/dist/core/agent/permission-manager.js +32 -0
- package/dist/core/agent/todo-manager.d.ts +26 -0
- package/dist/core/agent/todo-manager.js +91 -0
- package/dist/core/agent/tool-runner.d.ts +9 -0
- package/dist/core/agent/tool-runner.js +45 -0
- package/dist/core/agent.d.ts +228 -62
- package/dist/core/agent.js +1890 -615
- package/dist/core/config.d.ts +10 -0
- package/dist/core/config.js +2 -0
- package/dist/core/context-manager.d.ts +82 -0
- package/dist/core/context-manager.js +241 -0
- package/dist/core/errors.d.ts +22 -0
- package/dist/core/errors.js +49 -0
- package/dist/core/events.d.ts +41 -10
- package/dist/core/events.js +270 -68
- package/dist/core/file-pool.d.ts +41 -0
- package/dist/core/file-pool.js +102 -0
- package/dist/core/hooks.d.ts +3 -3
- package/dist/core/hooks.js +1 -1
- package/dist/core/permission-modes.d.ts +31 -0
- package/dist/core/permission-modes.js +61 -0
- package/dist/core/pool.d.ts +56 -13
- package/dist/core/pool.js +244 -34
- package/dist/core/room.d.ts +2 -2
- package/dist/core/room.js +10 -10
- package/dist/core/scheduler.d.ts +30 -23
- package/dist/core/scheduler.js +42 -168
- package/dist/core/skills/index.d.ts +10 -0
- package/dist/core/skills/index.js +20 -0
- package/dist/core/skills/management-manager.d.ts +130 -0
- package/dist/core/skills/management-manager.js +557 -0
- package/dist/core/skills/manager.d.ts +47 -0
- package/dist/core/skills/manager.js +243 -0
- package/dist/core/skills/operation-queue.d.ts +87 -0
- package/dist/core/skills/operation-queue.js +113 -0
- package/dist/core/skills/sandbox-file-manager.d.ts +82 -0
- package/dist/core/skills/sandbox-file-manager.js +183 -0
- package/dist/core/skills/types.d.ts +120 -0
- package/dist/core/skills/types.js +9 -0
- package/dist/core/skills/xml-generator.d.ts +13 -0
- package/dist/core/skills/xml-generator.js +70 -0
- package/dist/core/template.d.ts +57 -0
- package/dist/core/template.js +35 -0
- package/dist/core/time-bridge.d.ts +18 -0
- package/dist/core/time-bridge.js +100 -0
- package/dist/core/todo.d.ts +34 -0
- package/dist/core/todo.js +89 -0
- package/dist/core/types.d.ts +311 -114
- package/dist/core/types.js +1 -12
- package/dist/index.d.ts +47 -9
- package/dist/index.js +108 -15
- package/dist/infra/db/postgres/postgres-store.d.ts +97 -0
- package/dist/infra/db/postgres/postgres-store.js +1073 -0
- package/dist/infra/db/sqlite/sqlite-store.d.ts +84 -0
- package/dist/infra/db/sqlite/sqlite-store.js +800 -0
- package/dist/infra/e2b/e2b-fs.d.ts +29 -0
- package/dist/infra/e2b/e2b-fs.js +128 -0
- package/dist/infra/e2b/e2b-sandbox.d.ts +37 -0
- package/dist/infra/e2b/e2b-sandbox.js +156 -0
- package/dist/infra/e2b/e2b-template.d.ts +24 -0
- package/dist/infra/e2b/e2b-template.js +105 -0
- package/dist/infra/e2b/index.d.ts +4 -0
- package/dist/infra/e2b/index.js +9 -0
- package/dist/infra/e2b/types.d.ts +46 -0
- package/dist/infra/e2b/types.js +2 -0
- package/dist/infra/provider.d.ts +17 -58
- package/dist/infra/provider.js +65 -116
- package/dist/infra/providers/anthropic.d.ts +42 -0
- package/dist/infra/providers/anthropic.js +308 -0
- package/dist/infra/providers/core/errors.d.ts +230 -0
- package/dist/infra/providers/core/errors.js +353 -0
- package/dist/infra/providers/core/fork.d.ts +106 -0
- package/dist/infra/providers/core/fork.js +418 -0
- package/dist/infra/providers/core/index.d.ts +10 -0
- package/dist/infra/providers/core/index.js +76 -0
- package/dist/infra/providers/core/logger.d.ts +186 -0
- package/dist/infra/providers/core/logger.js +191 -0
- package/dist/infra/providers/core/retry.d.ts +62 -0
- package/dist/infra/providers/core/retry.js +189 -0
- package/dist/infra/providers/core/usage.d.ts +151 -0
- package/dist/infra/providers/core/usage.js +376 -0
- package/dist/infra/providers/gemini.d.ts +49 -0
- package/dist/infra/providers/gemini.js +493 -0
- package/dist/infra/providers/index.d.ts +25 -0
- package/dist/infra/providers/index.js +83 -0
- package/dist/infra/providers/openai.d.ts +123 -0
- package/dist/infra/providers/openai.js +662 -0
- package/dist/infra/providers/types.d.ts +334 -0
- package/dist/infra/providers/types.js +20 -0
- package/dist/infra/providers/utils.d.ts +53 -0
- package/dist/infra/providers/utils.js +400 -0
- package/dist/infra/sandbox-factory.d.ts +13 -0
- package/dist/infra/sandbox-factory.js +30 -0
- package/dist/infra/sandbox.d.ts +35 -6
- package/dist/infra/sandbox.js +174 -8
- package/dist/infra/store/factory.d.ts +45 -0
- package/dist/infra/store/factory.js +80 -0
- package/dist/infra/store/index.d.ts +3 -0
- package/dist/infra/store/index.js +26 -0
- package/dist/infra/store/json-store.d.ts +67 -0
- package/dist/infra/store/json-store.js +606 -0
- package/dist/infra/store/types.d.ts +342 -0
- package/dist/infra/store/types.js +2 -0
- package/dist/infra/store.d.ts +12 -32
- package/dist/infra/store.js +27 -130
- package/dist/tools/bash_kill/index.d.ts +1 -0
- package/dist/tools/bash_kill/index.js +35 -0
- package/dist/tools/bash_kill/prompt.d.ts +2 -0
- package/dist/tools/bash_kill/prompt.js +14 -0
- package/dist/tools/bash_logs/index.d.ts +1 -0
- package/dist/tools/bash_logs/index.js +40 -0
- package/dist/tools/bash_logs/prompt.d.ts +2 -0
- package/dist/tools/bash_logs/prompt.js +14 -0
- package/dist/tools/bash_run/index.d.ts +16 -0
- package/dist/tools/bash_run/index.js +61 -0
- package/dist/tools/bash_run/prompt.d.ts +2 -0
- package/dist/tools/bash_run/prompt.js +18 -0
- package/dist/tools/builtin.d.ts +7 -13
- package/dist/tools/builtin.js +19 -90
- package/dist/tools/define.d.ts +101 -0
- package/dist/tools/define.js +214 -0
- package/dist/tools/fs_edit/index.d.ts +1 -0
- package/dist/tools/fs_edit/index.js +62 -0
- package/dist/tools/fs_edit/prompt.d.ts +2 -0
- package/dist/tools/fs_edit/prompt.js +15 -0
- package/dist/tools/fs_glob/index.d.ts +1 -0
- package/dist/tools/fs_glob/index.js +40 -0
- package/dist/tools/fs_glob/prompt.d.ts +2 -0
- package/dist/tools/fs_glob/prompt.js +15 -0
- package/dist/tools/fs_grep/index.d.ts +1 -0
- package/dist/tools/fs_grep/index.js +66 -0
- package/dist/tools/fs_grep/prompt.d.ts +2 -0
- package/dist/tools/fs_grep/prompt.js +16 -0
- package/dist/tools/fs_multi_edit/index.d.ts +1 -0
- package/dist/tools/fs_multi_edit/index.js +106 -0
- package/dist/tools/fs_multi_edit/prompt.d.ts +2 -0
- package/dist/tools/fs_multi_edit/prompt.js +16 -0
- package/dist/tools/fs_read/index.d.ts +1 -0
- package/dist/tools/fs_read/index.js +40 -0
- package/dist/tools/fs_read/prompt.d.ts +2 -0
- package/dist/tools/fs_read/prompt.js +16 -0
- package/dist/tools/fs_write/index.d.ts +1 -0
- package/dist/tools/fs_write/index.js +40 -0
- package/dist/tools/fs_write/prompt.d.ts +2 -0
- package/dist/tools/fs_write/prompt.js +15 -0
- package/dist/tools/index.d.ts +11 -0
- package/dist/tools/index.js +61 -0
- package/dist/tools/mcp.d.ts +69 -0
- package/dist/tools/mcp.js +185 -0
- package/dist/tools/registry.d.ts +29 -0
- package/dist/tools/registry.js +26 -0
- package/dist/tools/scripts.d.ts +22 -0
- package/dist/tools/scripts.js +205 -0
- package/dist/tools/skills.d.ts +20 -0
- package/dist/tools/skills.js +115 -0
- package/dist/tools/task_run/index.d.ts +7 -0
- package/dist/tools/task_run/index.js +58 -0
- package/dist/tools/task_run/prompt.d.ts +5 -0
- package/dist/tools/task_run/prompt.js +25 -0
- package/dist/tools/todo_read/index.d.ts +1 -0
- package/dist/tools/todo_read/index.js +29 -0
- package/dist/tools/todo_read/prompt.d.ts +2 -0
- package/dist/tools/todo_read/prompt.js +18 -0
- package/dist/tools/todo_write/index.d.ts +1 -0
- package/dist/tools/todo_write/index.js +42 -0
- package/dist/tools/todo_write/prompt.d.ts +2 -0
- package/dist/tools/todo_write/prompt.js +23 -0
- package/dist/tools/tool.d.ts +43 -0
- package/dist/tools/tool.js +211 -0
- package/dist/tools/toolkit.d.ts +69 -0
- package/dist/tools/toolkit.js +98 -0
- package/dist/tools/type-inference.d.ts +127 -0
- package/dist/tools/type-inference.js +207 -0
- package/dist/utils/agent-id.d.ts +1 -0
- package/dist/utils/agent-id.js +28 -0
- package/dist/utils/logger.d.ts +15 -0
- package/dist/utils/logger.js +44 -0
- package/dist/utils/session-id.js +16 -16
- package/package.json +35 -11
- package/dist/tools/bash.d.ts +0 -63
- package/dist/tools/bash.js +0 -92
- package/dist/tools/fs.d.ts +0 -96
- package/dist/tools/fs.js +0 -100
- package/dist/tools/task.d.ts +0 -38
- package/dist/tools/task.js +0 -45
package/dist/core/events.js
CHANGED
|
@@ -1,89 +1,289 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.EventBus = void 0;
|
|
4
|
-
const types_1 = require("../core/types");
|
|
5
4
|
const events_1 = require("events");
|
|
6
|
-
const
|
|
7
|
-
class EventBus
|
|
5
|
+
const logger_1 = require("../utils/logger");
|
|
6
|
+
class EventBus {
|
|
8
7
|
constructor() {
|
|
9
|
-
super(...arguments);
|
|
10
8
|
this.cursor = 0;
|
|
9
|
+
this.seq = 0;
|
|
11
10
|
this.timeline = [];
|
|
12
|
-
this.subscribers = new
|
|
11
|
+
this.subscribers = new Map();
|
|
12
|
+
this.controlEmitter = new events_1.EventEmitter();
|
|
13
|
+
this.monitorEmitter = new events_1.EventEmitter();
|
|
14
|
+
this.failedEvents = [];
|
|
15
|
+
this.MAX_FAILED_BUFFER = 1000;
|
|
16
|
+
this.subscribers.set('progress', new Set());
|
|
17
|
+
this.subscribers.set('control', new Set());
|
|
18
|
+
this.subscribers.set('monitor', new Set());
|
|
19
|
+
// Prevent Node.js ERR_UNHANDLED_ERROR when emitting 'error' events
|
|
20
|
+
// without a listener. This allows MonitorEvent with type='error' to be
|
|
21
|
+
// emitted safely. Users can still register their own 'error' handlers.
|
|
22
|
+
this.monitorEmitter.on('error', () => {
|
|
23
|
+
// No-op: errors are handled through the subscriber system
|
|
24
|
+
});
|
|
13
25
|
}
|
|
14
|
-
setStore(store,
|
|
26
|
+
setStore(store, agentId) {
|
|
15
27
|
this.store = store;
|
|
16
|
-
this.
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
this.
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
// Emit control plane events
|
|
43
|
-
this.emit(event.type, fullEvent);
|
|
44
|
-
return cursor;
|
|
45
|
-
}
|
|
46
|
-
subscribe(opts) {
|
|
47
|
-
const subscriber = new EventSubscriber(opts?.kinds || types_1.MINIMAL_EVENT_KINDS);
|
|
48
|
-
this.subscribers.add(subscriber);
|
|
49
|
-
// Replay past events if since is specified
|
|
50
|
-
if (opts?.since !== undefined) {
|
|
51
|
-
const past = this.timeline.filter((t) => t.cursor >= opts.since);
|
|
52
|
-
for (const t of past) {
|
|
53
|
-
if (subscriber.accepts(t.event.type)) {
|
|
54
|
-
subscriber.push(t.event);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
28
|
+
this.agentId = agentId;
|
|
29
|
+
}
|
|
30
|
+
emitProgress(event) {
|
|
31
|
+
const envelope = this.emit('progress', event);
|
|
32
|
+
this.notifySubscribers('progress', envelope);
|
|
33
|
+
return envelope;
|
|
34
|
+
}
|
|
35
|
+
emitControl(event) {
|
|
36
|
+
const envelope = this.emit('control', event);
|
|
37
|
+
this.controlEmitter.emit(event.type, envelope.event);
|
|
38
|
+
this.notifySubscribers('control', envelope);
|
|
39
|
+
return envelope;
|
|
40
|
+
}
|
|
41
|
+
emitMonitor(event) {
|
|
42
|
+
const envelope = this.emit('monitor', event);
|
|
43
|
+
this.monitorEmitter.emit(event.type, envelope.event);
|
|
44
|
+
this.notifySubscribers('monitor', envelope);
|
|
45
|
+
return envelope;
|
|
46
|
+
}
|
|
47
|
+
subscribeProgress(opts) {
|
|
48
|
+
const subscriber = new EventSubscriber(opts?.kinds);
|
|
49
|
+
this.subscribers.get('progress').add(subscriber);
|
|
50
|
+
if (opts?.since) {
|
|
51
|
+
void this.replayHistory('progress', subscriber, opts.since);
|
|
57
52
|
}
|
|
58
|
-
const
|
|
53
|
+
const bus = this;
|
|
59
54
|
return {
|
|
60
|
-
[Symbol.asyncIterator]
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
55
|
+
[Symbol.asyncIterator]() {
|
|
56
|
+
return {
|
|
57
|
+
async next() {
|
|
58
|
+
const value = (await subscriber.next());
|
|
59
|
+
if (!value) {
|
|
60
|
+
bus.subscribers.get('progress').delete(subscriber);
|
|
61
|
+
return { done: true, value: undefined };
|
|
62
|
+
}
|
|
63
|
+
return { done: false, value };
|
|
64
|
+
},
|
|
65
|
+
async return() {
|
|
66
|
+
subscriber.close();
|
|
67
|
+
bus.subscribers.get('progress').delete(subscriber);
|
|
65
68
|
return { done: true, value: undefined };
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
return: async () => {
|
|
70
|
-
subscriber.close();
|
|
71
|
-
self.subscribers.delete(subscriber);
|
|
72
|
-
return { done: true, value: undefined };
|
|
73
|
-
},
|
|
74
|
-
}),
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
},
|
|
75
72
|
};
|
|
76
73
|
}
|
|
74
|
+
subscribe(channels = ['progress', 'control', 'monitor'], opts) {
|
|
75
|
+
const subscriber = new EventSubscriber(opts?.kinds);
|
|
76
|
+
for (const channel of channels) {
|
|
77
|
+
this.subscribers.get(channel).add(subscriber);
|
|
78
|
+
if (opts?.since) {
|
|
79
|
+
void this.replayHistory(channel, subscriber, opts.since);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return this.iterableFor(channels, subscriber);
|
|
83
|
+
}
|
|
84
|
+
onControl(type, handler) {
|
|
85
|
+
this.controlEmitter.on(type, handler);
|
|
86
|
+
return () => this.controlEmitter.off(type, handler);
|
|
87
|
+
}
|
|
88
|
+
onMonitor(type, handler) {
|
|
89
|
+
this.monitorEmitter.on(type, handler);
|
|
90
|
+
return () => this.monitorEmitter.off(type, handler);
|
|
91
|
+
}
|
|
77
92
|
getTimeline(since) {
|
|
78
93
|
return since !== undefined ? this.timeline.filter((t) => t.cursor >= since) : this.timeline;
|
|
79
94
|
}
|
|
80
95
|
getCursor() {
|
|
81
96
|
return this.cursor;
|
|
82
97
|
}
|
|
98
|
+
getLastBookmark() {
|
|
99
|
+
const last = this.timeline[this.timeline.length - 1];
|
|
100
|
+
return last?.bookmark;
|
|
101
|
+
}
|
|
102
|
+
syncCursor(bookmark) {
|
|
103
|
+
if (!bookmark)
|
|
104
|
+
return;
|
|
105
|
+
const nextSeq = bookmark.seq + 1;
|
|
106
|
+
if (this.seq < nextSeq) {
|
|
107
|
+
this.seq = nextSeq;
|
|
108
|
+
}
|
|
109
|
+
const timelineCursor = this.timeline.length
|
|
110
|
+
? this.timeline[this.timeline.length - 1].cursor + 1
|
|
111
|
+
: 0;
|
|
112
|
+
const nextCursor = Math.max(this.cursor, nextSeq, timelineCursor);
|
|
113
|
+
if (this.cursor < nextCursor) {
|
|
114
|
+
this.cursor = nextCursor;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
83
117
|
reset() {
|
|
84
118
|
this.cursor = 0;
|
|
119
|
+
this.seq = 0;
|
|
85
120
|
this.timeline = [];
|
|
86
|
-
this.subscribers.
|
|
121
|
+
for (const set of this.subscribers.values()) {
|
|
122
|
+
set.clear();
|
|
123
|
+
}
|
|
124
|
+
this.controlEmitter.removeAllListeners();
|
|
125
|
+
this.monitorEmitter.removeAllListeners();
|
|
126
|
+
}
|
|
127
|
+
emit(channel, event) {
|
|
128
|
+
const bookmark = {
|
|
129
|
+
seq: this.seq++,
|
|
130
|
+
timestamp: Date.now(),
|
|
131
|
+
};
|
|
132
|
+
const eventWithChannel = { ...event, channel };
|
|
133
|
+
const eventWithBookmark = { ...eventWithChannel, bookmark };
|
|
134
|
+
const envelope = {
|
|
135
|
+
cursor: this.cursor++,
|
|
136
|
+
bookmark,
|
|
137
|
+
event: eventWithBookmark,
|
|
138
|
+
};
|
|
139
|
+
const timelineEntry = {
|
|
140
|
+
cursor: envelope.cursor,
|
|
141
|
+
bookmark,
|
|
142
|
+
event: envelope.event,
|
|
143
|
+
};
|
|
144
|
+
this.timeline.push(timelineEntry);
|
|
145
|
+
if (this.timeline.length > 10000) {
|
|
146
|
+
this.timeline = this.timeline.slice(-5000);
|
|
147
|
+
}
|
|
148
|
+
if (this.store && this.agentId) {
|
|
149
|
+
const isCritical = this.isCriticalEvent(event);
|
|
150
|
+
this.store.appendEvent(this.agentId, timelineEntry)
|
|
151
|
+
.then(() => {
|
|
152
|
+
// 成功后尝试重试之前失败的事件
|
|
153
|
+
if (this.failedEvents.length > 0) {
|
|
154
|
+
void this.retryFailedEvents();
|
|
155
|
+
}
|
|
156
|
+
})
|
|
157
|
+
.catch((err) => {
|
|
158
|
+
if (isCritical) {
|
|
159
|
+
// 关键事件失败:缓存到内存
|
|
160
|
+
this.failedEvents.push(timelineEntry);
|
|
161
|
+
if (this.failedEvents.length > this.MAX_FAILED_BUFFER) {
|
|
162
|
+
this.failedEvents = this.failedEvents.slice(-this.MAX_FAILED_BUFFER);
|
|
163
|
+
}
|
|
164
|
+
// 发送降级的内存 Monitor 事件(不持久化)
|
|
165
|
+
try {
|
|
166
|
+
this.monitorEmitter.emit('storage_failure', {
|
|
167
|
+
type: 'storage_failure',
|
|
168
|
+
severity: 'critical',
|
|
169
|
+
failedEvent: event.type,
|
|
170
|
+
bufferedCount: this.failedEvents.length,
|
|
171
|
+
error: err.message
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
// 降级事件发送失败也不阻塞
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
// 非关键事件失败:仅记录日志
|
|
180
|
+
logger_1.logger.warn(`[EventBus] Failed to persist non-critical event: ${event.type}`, err);
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
return envelope;
|
|
185
|
+
}
|
|
186
|
+
isCriticalEvent(event) {
|
|
187
|
+
const criticalTypes = new Set([
|
|
188
|
+
'tool:end',
|
|
189
|
+
'done',
|
|
190
|
+
'permission_decided',
|
|
191
|
+
'agent_resumed',
|
|
192
|
+
'state_changed',
|
|
193
|
+
'breakpoint_changed',
|
|
194
|
+
'error',
|
|
195
|
+
]);
|
|
196
|
+
return criticalTypes.has(event.type);
|
|
197
|
+
}
|
|
198
|
+
async retryFailedEvents() {
|
|
199
|
+
if (!this.store || !this.agentId || this.failedEvents.length === 0)
|
|
200
|
+
return;
|
|
201
|
+
const toRetry = this.failedEvents.splice(0, 10);
|
|
202
|
+
for (const event of toRetry) {
|
|
203
|
+
try {
|
|
204
|
+
await this.store.appendEvent(this.agentId, event);
|
|
205
|
+
}
|
|
206
|
+
catch (err) {
|
|
207
|
+
this.failedEvents.unshift(event);
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
getFailedEventCount() {
|
|
213
|
+
return this.failedEvents.length;
|
|
214
|
+
}
|
|
215
|
+
async flushFailedEvents() {
|
|
216
|
+
while (this.failedEvents.length > 0) {
|
|
217
|
+
await this.retryFailedEvents();
|
|
218
|
+
if (this.failedEvents.length > 0) {
|
|
219
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
notifySubscribers(channel, envelope) {
|
|
224
|
+
const subscribers = this.subscribers.get(channel);
|
|
225
|
+
if (!subscribers)
|
|
226
|
+
return;
|
|
227
|
+
for (const subscriber of subscribers) {
|
|
228
|
+
if (subscriber.accepts(envelope)) {
|
|
229
|
+
subscriber.push(envelope);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
async replayHistory(channel, subscriber, since) {
|
|
234
|
+
if (this.store && this.agentId) {
|
|
235
|
+
try {
|
|
236
|
+
const opts = { channel: channel, since };
|
|
237
|
+
for await (const entry of this.store.readEvents(this.agentId, opts)) {
|
|
238
|
+
const envelope = entry;
|
|
239
|
+
if (subscriber.accepts(envelope)) {
|
|
240
|
+
subscriber.push(envelope);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
catch (error) {
|
|
246
|
+
logger_1.logger.error('Failed to replay events from store:', error);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
const past = this.timeline.filter((t) => {
|
|
250
|
+
if (t.event.channel !== channel)
|
|
251
|
+
return false;
|
|
252
|
+
if (!since)
|
|
253
|
+
return true;
|
|
254
|
+
return t.bookmark.seq > since.seq;
|
|
255
|
+
});
|
|
256
|
+
for (const entry of past) {
|
|
257
|
+
const envelope = entry;
|
|
258
|
+
if (subscriber.accepts(envelope)) {
|
|
259
|
+
subscriber.push(envelope);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
iterableFor(channel, subscriber) {
|
|
264
|
+
const channels = Array.isArray(channel) ? channel : [channel];
|
|
265
|
+
const bus = this;
|
|
266
|
+
return {
|
|
267
|
+
[Symbol.asyncIterator]() {
|
|
268
|
+
return {
|
|
269
|
+
next: async () => {
|
|
270
|
+
const value = (await subscriber.next());
|
|
271
|
+
if (!value) {
|
|
272
|
+
for (const ch of channels)
|
|
273
|
+
bus.subscribers.get(ch).delete(subscriber);
|
|
274
|
+
return { done: true, value: undefined };
|
|
275
|
+
}
|
|
276
|
+
return { done: false, value };
|
|
277
|
+
},
|
|
278
|
+
return: async () => {
|
|
279
|
+
subscriber.close();
|
|
280
|
+
for (const ch of channels)
|
|
281
|
+
bus.subscribers.get(ch).delete(subscriber);
|
|
282
|
+
return { done: true, value: undefined };
|
|
283
|
+
},
|
|
284
|
+
};
|
|
285
|
+
},
|
|
286
|
+
};
|
|
87
287
|
}
|
|
88
288
|
}
|
|
89
289
|
exports.EventBus = EventBus;
|
|
@@ -94,18 +294,20 @@ class EventSubscriber {
|
|
|
94
294
|
this.waiting = null;
|
|
95
295
|
this.closed = false;
|
|
96
296
|
}
|
|
97
|
-
accepts(
|
|
98
|
-
|
|
297
|
+
accepts(envelope) {
|
|
298
|
+
if (!this.kinds || this.kinds.length === 0)
|
|
299
|
+
return true;
|
|
300
|
+
return this.kinds.includes(String(envelope.event.type));
|
|
99
301
|
}
|
|
100
|
-
push(
|
|
302
|
+
push(envelope) {
|
|
101
303
|
if (this.closed)
|
|
102
304
|
return;
|
|
103
305
|
if (this.waiting) {
|
|
104
|
-
this.waiting(
|
|
306
|
+
this.waiting(envelope);
|
|
105
307
|
this.waiting = null;
|
|
106
308
|
}
|
|
107
309
|
else {
|
|
108
|
-
this.queue.push(
|
|
310
|
+
this.queue.push(envelope);
|
|
109
311
|
}
|
|
110
312
|
}
|
|
111
313
|
async next() {
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Sandbox } from '../infra/sandbox';
|
|
2
|
+
export interface FileRecord {
|
|
3
|
+
path: string;
|
|
4
|
+
lastRead?: number;
|
|
5
|
+
lastEdit?: number;
|
|
6
|
+
lastReadMtime?: number;
|
|
7
|
+
lastKnownMtime?: number;
|
|
8
|
+
}
|
|
9
|
+
export interface FileFreshness {
|
|
10
|
+
isFresh: boolean;
|
|
11
|
+
lastRead?: number;
|
|
12
|
+
lastEdit?: number;
|
|
13
|
+
currentMtime?: number;
|
|
14
|
+
}
|
|
15
|
+
interface FilePoolOptions {
|
|
16
|
+
watch?: boolean;
|
|
17
|
+
onChange?: (event: {
|
|
18
|
+
path: string;
|
|
19
|
+
mtime: number;
|
|
20
|
+
}) => void;
|
|
21
|
+
}
|
|
22
|
+
export declare class FilePool {
|
|
23
|
+
private readonly sandbox;
|
|
24
|
+
private records;
|
|
25
|
+
private watchers;
|
|
26
|
+
private readonly watchEnabled;
|
|
27
|
+
private readonly onChange?;
|
|
28
|
+
constructor(sandbox: Sandbox, opts?: FilePoolOptions);
|
|
29
|
+
private getMtime;
|
|
30
|
+
recordRead(path: string): Promise<void>;
|
|
31
|
+
recordEdit(path: string): Promise<void>;
|
|
32
|
+
validateWrite(path: string): Promise<FileFreshness>;
|
|
33
|
+
checkFreshness(path: string): Promise<FileFreshness>;
|
|
34
|
+
getTrackedFiles(): string[];
|
|
35
|
+
private ensureWatch;
|
|
36
|
+
getAccessedFiles(): Array<{
|
|
37
|
+
path: string;
|
|
38
|
+
mtime: number;
|
|
39
|
+
}>;
|
|
40
|
+
}
|
|
41
|
+
export {};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FilePool = void 0;
|
|
4
|
+
const logger_1 = require("../utils/logger");
|
|
5
|
+
class FilePool {
|
|
6
|
+
constructor(sandbox, opts) {
|
|
7
|
+
this.sandbox = sandbox;
|
|
8
|
+
this.records = new Map();
|
|
9
|
+
this.watchers = new Map();
|
|
10
|
+
this.watchEnabled = opts?.watch ?? true;
|
|
11
|
+
this.onChange = opts?.onChange;
|
|
12
|
+
}
|
|
13
|
+
async getMtime(path) {
|
|
14
|
+
try {
|
|
15
|
+
const stat = await this.sandbox.fs.stat(path);
|
|
16
|
+
return stat.mtimeMs;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
async recordRead(path) {
|
|
23
|
+
const resolved = this.sandbox.fs.resolve(path);
|
|
24
|
+
const record = this.records.get(resolved) || { path: resolved };
|
|
25
|
+
record.lastRead = Date.now();
|
|
26
|
+
record.lastReadMtime = await this.getMtime(resolved);
|
|
27
|
+
record.lastKnownMtime = record.lastReadMtime;
|
|
28
|
+
this.records.set(resolved, record);
|
|
29
|
+
await this.ensureWatch(resolved);
|
|
30
|
+
}
|
|
31
|
+
async recordEdit(path) {
|
|
32
|
+
const resolved = this.sandbox.fs.resolve(path);
|
|
33
|
+
const record = this.records.get(resolved) || { path: resolved };
|
|
34
|
+
record.lastEdit = Date.now();
|
|
35
|
+
record.lastKnownMtime = await this.getMtime(resolved);
|
|
36
|
+
this.records.set(resolved, record);
|
|
37
|
+
await this.ensureWatch(resolved);
|
|
38
|
+
}
|
|
39
|
+
async validateWrite(path) {
|
|
40
|
+
const resolved = this.sandbox.fs.resolve(path);
|
|
41
|
+
const record = this.records.get(resolved);
|
|
42
|
+
const currentMtime = await this.getMtime(resolved);
|
|
43
|
+
if (!record) {
|
|
44
|
+
return { isFresh: true, currentMtime };
|
|
45
|
+
}
|
|
46
|
+
const isFresh = record.lastRead !== undefined &&
|
|
47
|
+
(currentMtime === undefined || record.lastReadMtime === undefined || currentMtime === record.lastReadMtime);
|
|
48
|
+
return {
|
|
49
|
+
isFresh,
|
|
50
|
+
lastRead: record.lastRead,
|
|
51
|
+
lastEdit: record.lastEdit,
|
|
52
|
+
currentMtime,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
async checkFreshness(path) {
|
|
56
|
+
const resolved = this.sandbox.fs.resolve(path);
|
|
57
|
+
const record = this.records.get(resolved);
|
|
58
|
+
const currentMtime = await this.getMtime(resolved);
|
|
59
|
+
if (!record) {
|
|
60
|
+
return { isFresh: false, currentMtime };
|
|
61
|
+
}
|
|
62
|
+
const isFresh = record.lastRead !== undefined &&
|
|
63
|
+
(currentMtime === undefined || record.lastKnownMtime === undefined || currentMtime === record.lastKnownMtime);
|
|
64
|
+
return {
|
|
65
|
+
isFresh,
|
|
66
|
+
lastRead: record.lastRead,
|
|
67
|
+
lastEdit: record.lastEdit,
|
|
68
|
+
currentMtime,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
getTrackedFiles() {
|
|
72
|
+
return Array.from(this.records.keys());
|
|
73
|
+
}
|
|
74
|
+
async ensureWatch(path) {
|
|
75
|
+
if (!this.watchEnabled)
|
|
76
|
+
return;
|
|
77
|
+
if (!this.sandbox.watchFiles)
|
|
78
|
+
return;
|
|
79
|
+
if (this.watchers.has(path))
|
|
80
|
+
return;
|
|
81
|
+
try {
|
|
82
|
+
const id = await this.sandbox.watchFiles([path], (event) => {
|
|
83
|
+
const record = this.records.get(path);
|
|
84
|
+
if (record) {
|
|
85
|
+
record.lastKnownMtime = event.mtimeMs;
|
|
86
|
+
}
|
|
87
|
+
this.onChange?.({ path, mtime: event.mtimeMs });
|
|
88
|
+
});
|
|
89
|
+
this.watchers.set(path, id);
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
// 记录 watch 失败,但不中断流程
|
|
93
|
+
logger_1.logger.warn(`[FilePool] Failed to watch file: ${path}`, err);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
getAccessedFiles() {
|
|
97
|
+
return Array.from(this.records.values())
|
|
98
|
+
.filter((r) => r.lastKnownMtime !== undefined)
|
|
99
|
+
.map((r) => ({ path: r.path, mtime: r.lastKnownMtime }));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
exports.FilePool = FilePool;
|
package/dist/core/hooks.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { ToolCall, ToolOutcome, HookDecision, PostHookResult, ToolContext } from '../core/types';
|
|
2
|
-
import {
|
|
2
|
+
import { ModelResponse } from '../infra/provider';
|
|
3
3
|
export interface Hooks {
|
|
4
4
|
preToolUse?: (call: ToolCall, ctx: ToolContext) => HookDecision | Promise<HookDecision>;
|
|
5
5
|
postToolUse?: (outcome: ToolOutcome, ctx: ToolContext) => PostHookResult | Promise<PostHookResult>;
|
|
6
6
|
preModel?: (request: any) => void | Promise<void>;
|
|
7
|
-
postModel?: (response:
|
|
7
|
+
postModel?: (response: ModelResponse) => void | Promise<void>;
|
|
8
8
|
messagesChanged?: (snapshot: any) => void | Promise<void>;
|
|
9
9
|
}
|
|
10
10
|
export interface RegisteredHook {
|
|
@@ -18,6 +18,6 @@ export declare class HookManager {
|
|
|
18
18
|
runPreToolUse(call: ToolCall, ctx: ToolContext): Promise<HookDecision>;
|
|
19
19
|
runPostToolUse(outcome: ToolOutcome, ctx: ToolContext): Promise<ToolOutcome>;
|
|
20
20
|
runPreModel(request: any): Promise<void>;
|
|
21
|
-
runPostModel(response:
|
|
21
|
+
runPostModel(response: ModelResponse): Promise<void>;
|
|
22
22
|
runMessagesChanged(snapshot: any): Promise<void>;
|
|
23
23
|
}
|
package/dist/core/hooks.js
CHANGED
|
@@ -34,7 +34,7 @@ class HookManager {
|
|
|
34
34
|
for (const { hooks } of this.hooks) {
|
|
35
35
|
if (hooks.postToolUse) {
|
|
36
36
|
const result = await hooks.postToolUse(current, ctx);
|
|
37
|
-
if (result) {
|
|
37
|
+
if (result && typeof result === 'object') {
|
|
38
38
|
if ('replace' in result) {
|
|
39
39
|
current = result.replace;
|
|
40
40
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { PermissionConfig } from './template';
|
|
2
|
+
import { ToolDescriptor } from '../tools/registry';
|
|
3
|
+
export type PermissionDecision = 'allow' | 'deny' | 'ask';
|
|
4
|
+
export interface PermissionEvaluationContext {
|
|
5
|
+
toolName: string;
|
|
6
|
+
descriptor?: ToolDescriptor;
|
|
7
|
+
config: PermissionConfig;
|
|
8
|
+
}
|
|
9
|
+
export type PermissionModeHandler = (ctx: PermissionEvaluationContext) => PermissionDecision;
|
|
10
|
+
export interface SerializedPermissionMode {
|
|
11
|
+
name: string;
|
|
12
|
+
builtIn: boolean;
|
|
13
|
+
}
|
|
14
|
+
export declare class PermissionModeRegistry {
|
|
15
|
+
private handlers;
|
|
16
|
+
private customModes;
|
|
17
|
+
register(mode: string, handler: PermissionModeHandler, isBuiltIn?: boolean): void;
|
|
18
|
+
get(mode: string): PermissionModeHandler | undefined;
|
|
19
|
+
list(): string[];
|
|
20
|
+
/**
|
|
21
|
+
* 序列化权限模式配置
|
|
22
|
+
* 仅序列化自定义模式的名称,内置模式在 Resume 时自动恢复
|
|
23
|
+
*/
|
|
24
|
+
serialize(): SerializedPermissionMode[];
|
|
25
|
+
/**
|
|
26
|
+
* 验证序列化的权限模式是否可恢复
|
|
27
|
+
* 返回缺失的自定义模式列表
|
|
28
|
+
*/
|
|
29
|
+
validateRestore(serialized: SerializedPermissionMode[]): string[];
|
|
30
|
+
}
|
|
31
|
+
export declare const permissionModes: PermissionModeRegistry;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.permissionModes = exports.PermissionModeRegistry = void 0;
|
|
4
|
+
class PermissionModeRegistry {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.handlers = new Map();
|
|
7
|
+
this.customModes = new Set();
|
|
8
|
+
}
|
|
9
|
+
register(mode, handler, isBuiltIn = false) {
|
|
10
|
+
this.handlers.set(mode, handler);
|
|
11
|
+
if (!isBuiltIn) {
|
|
12
|
+
this.customModes.add(mode);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
get(mode) {
|
|
16
|
+
return this.handlers.get(mode);
|
|
17
|
+
}
|
|
18
|
+
list() {
|
|
19
|
+
return Array.from(this.handlers.keys());
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* 序列化权限模式配置
|
|
23
|
+
* 仅序列化自定义模式的名称,内置模式在 Resume 时自动恢复
|
|
24
|
+
*/
|
|
25
|
+
serialize() {
|
|
26
|
+
return Array.from(this.handlers.keys()).map(name => ({
|
|
27
|
+
name,
|
|
28
|
+
builtIn: !this.customModes.has(name)
|
|
29
|
+
}));
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* 验证序列化的权限模式是否可恢复
|
|
33
|
+
* 返回缺失的自定义模式列表
|
|
34
|
+
*/
|
|
35
|
+
validateRestore(serialized) {
|
|
36
|
+
const missing = [];
|
|
37
|
+
for (const mode of serialized) {
|
|
38
|
+
if (!mode.builtIn && !this.handlers.has(mode.name)) {
|
|
39
|
+
missing.push(mode.name);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return missing;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
exports.PermissionModeRegistry = PermissionModeRegistry;
|
|
46
|
+
exports.permissionModes = new PermissionModeRegistry();
|
|
47
|
+
const MUTATING_ACCESS = new Set(['write', 'execute', 'manage', 'mutate']);
|
|
48
|
+
// 内置模式
|
|
49
|
+
exports.permissionModes.register('auto', () => 'allow', true);
|
|
50
|
+
exports.permissionModes.register('approval', () => 'ask', true);
|
|
51
|
+
exports.permissionModes.register('readonly', (ctx) => {
|
|
52
|
+
const metadata = ctx.descriptor?.metadata || {};
|
|
53
|
+
if (metadata.mutates === true)
|
|
54
|
+
return 'deny';
|
|
55
|
+
if (metadata.mutates === false)
|
|
56
|
+
return 'allow';
|
|
57
|
+
const access = typeof metadata.access === 'string' ? metadata.access.toLowerCase() : undefined;
|
|
58
|
+
if (access && MUTATING_ACCESS.has(access))
|
|
59
|
+
return 'deny';
|
|
60
|
+
return 'ask';
|
|
61
|
+
}, true);
|