ocean-crew 0.0.0-stage → 0.4.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 +156 -2
- package/THIRD_PARTY_NOTICES.md +159 -0
- package/client.js +31389 -0
- package/cordis.patch.yml +6 -0
- package/dist/host/chat/btw.js +436 -0
- package/dist/host/chat/channel.js +1241 -0
- package/dist/host/chat/chat-index.js +344 -0
- package/dist/host/chat/delivery.js +231 -0
- package/dist/host/chat/feed.js +165 -0
- package/dist/host/chat/handoffs.js +625 -0
- package/dist/host/chat/parts.js +2033 -0
- package/dist/host/chat/requests.js +155 -0
- package/dist/host/chat/resume.js +364 -0
- package/dist/host/chat/side.js +457 -0
- package/dist/host/chat/steer.js +346 -0
- package/dist/host/chat/turns.js +1457 -0
- package/dist/host/connectors/catalog.js +331 -0
- package/dist/host/connectors/gateway.js +339 -0
- package/dist/host/connectors/index.js +1235 -0
- package/dist/host/connectors/mail/index.js +912 -0
- package/dist/host/connectors/mail/presets.js +53 -0
- package/dist/host/connectors/mail/transport.js +519 -0
- package/dist/host/connectors/oauth.js +337 -0
- package/dist/host/core/constants.js +123 -0
- package/dist/host/core/diagnostics.js +310 -0
- package/dist/host/core/dsh-update.js +212 -0
- package/dist/host/core/events.js +16 -0
- package/dist/host/core/files.js +37 -0
- package/dist/host/core/folders.js +283 -0
- package/dist/host/core/home-lock.js +137 -0
- package/dist/host/core/links.js +52 -0
- package/dist/host/core/runtime.js +5 -0
- package/dist/host/core/store.js +662 -0
- package/dist/host/core/text.js +59 -0
- package/dist/host/core/thinking.js +60 -0
- package/dist/host/core/time-context.js +69 -0
- package/dist/host/core/update.js +466 -0
- package/dist/host/groups/calls.js +684 -0
- package/dist/host/groups/index.js +79 -0
- package/dist/host/groups/log.js +334 -0
- package/dist/host/groups/prompt.js +124 -0
- package/dist/host/groups/relay.js +489 -0
- package/dist/host/groups/round.js +992 -0
- package/dist/host/groups/tasks.js +217 -0
- package/dist/host/groups/tools.js +478 -0
- package/dist/host/groups/types.js +8 -0
- package/dist/host/im/format.js +228 -0
- package/dist/host/im/index.js +2461 -0
- package/dist/host/im/platforms/feishu.js +1134 -0
- package/dist/host/im/platforms/index.js +6 -0
- package/dist/host/im/platforms/media-type.js +70 -0
- package/dist/host/im/platforms/weixin.js +1117 -0
- package/dist/host/im/reaction.js +51 -0
- package/dist/host/im/supervisor.js +56 -0
- package/dist/host/team/agents.js +161 -0
- package/dist/host/team/models.js +427 -0
- package/dist/host/team/permissions.js +263 -0
- package/dist/host/team/privacy.js +108 -0
- package/dist/host/team/prompt.js +86 -0
- package/dist/host/team/providers.js +461 -0
- package/dist/host/team/roster.js +1011 -0
- package/dist/host/team/tools.js +1638 -0
- package/dist/host/work/approvals.js +290 -0
- package/dist/host/work/avatar-library.js +155 -0
- package/dist/host/work/avatar.js +132 -0
- package/dist/host/work/capabilities.js +862 -0
- package/dist/host/work/companion.js +306 -0
- package/dist/host/work/goal-skills.js +91 -0
- package/dist/host/work/goals.js +1337 -0
- package/dist/host/work/images.js +283 -0
- package/dist/host/work/learning.js +1021 -0
- package/dist/host/work/memory-pending.js +108 -0
- package/dist/host/work/memory.js +3237 -0
- package/dist/host/work/notify.js +146 -0
- package/dist/host/work/reset.js +43 -0
- package/dist/host/work/schedules.js +309 -0
- package/dist/host/work/secrets.js +702 -0
- package/dist/host/work/steward.js +166 -0
- package/dist/host/work/tts.js +192 -0
- package/dist/host/work/usage.js +84 -0
- package/dist/index.js +137 -0
- package/dist/shared/api.js +32 -0
- package/dist/shared/companion.js +110 -0
- package/dist/shared/config.js +3 -0
- package/dist/shared/coordination.js +6 -0
- package/dist/shared/group-replies.js +95 -0
- package/dist/shared/im-platforms.js +35 -0
- package/dist/shared/im.js +3 -0
- package/dist/shared/mentions.js +85 -0
- package/dist/shared/state.js +25 -0
- package/package.json +81 -5
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// The live feed: a Server-Sent Events stream of what changes, for a window that draws
|
|
2
|
+
// the team itself (the desktop app) instead of polling.
|
|
3
|
+
// hello the stream opened: the current revision
|
|
4
|
+
// revision the team was saved: ask for `state` again
|
|
5
|
+
// live work changed without a new revision: ask for `state` again
|
|
6
|
+
// message a new entry of a Bot's own chat, as the `chat` endpoint returns it
|
|
7
|
+
// stream the reply a Bot is writing: a start, its text as it comes, an end
|
|
8
|
+
//
|
|
9
|
+
// owns. Until that module is installed the feed carries hello/revision/live/stream, which
|
|
10
|
+
// is what a window needs when nothing but the route is wired yet.
|
|
11
|
+
import { ROUTE } from "../core/constants.js";
|
|
12
|
+
import { listenGlobal } from "../core/events.js";
|
|
13
|
+
export const FEED_ROUTE = `${ROUTE}/feed`;
|
|
14
|
+
// A comment line now and then keeps an idle connection open.
|
|
15
|
+
const KEEPALIVE_MS = 20_000;
|
|
16
|
+
// Status changes come in bursts (a turn starts several Sessions); one notice covers them.
|
|
17
|
+
const LIVE_DELAY_MS = 150;
|
|
18
|
+
// A window that stops reading is dropped once this many events wait for it; it reconnects
|
|
19
|
+
// and catches up from `hello`.
|
|
20
|
+
const MAX_QUEUED = 512;
|
|
21
|
+
export function install(rt) {
|
|
22
|
+
const { ctx, state } = rt;
|
|
23
|
+
const encoder = new TextEncoder();
|
|
24
|
+
const clients = new Set();
|
|
25
|
+
// A Request built on a caller's signal only follows it while the Request is reachable;
|
|
26
|
+
// once collected, its abort never fires and the feed stays open. Each open feed keeps
|
|
27
|
+
// its Request until it leaves.
|
|
28
|
+
const requests = new Map();
|
|
29
|
+
// Attempts being streamed, by id. A turn that answers another Bot is hidden from the
|
|
30
|
+
// chat (turns.ts marks it before the model is called), so its text is not streamed.
|
|
31
|
+
const writing = new Map();
|
|
32
|
+
const leave = (controller) => {
|
|
33
|
+
if (controller === undefined)
|
|
34
|
+
return;
|
|
35
|
+
clients.delete(controller);
|
|
36
|
+
requests.delete(controller);
|
|
37
|
+
if (clients.size === 0)
|
|
38
|
+
writing.clear();
|
|
39
|
+
try {
|
|
40
|
+
controller.close();
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// Already closed or errored.
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
const send = (controller, chunk) => {
|
|
47
|
+
if ((controller.desiredSize ?? 0) < -MAX_QUEUED) {
|
|
48
|
+
leave(controller);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
controller.enqueue(chunk);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// The stream was closed under us; drop the client so the next notice skips it.
|
|
56
|
+
clients.delete(controller);
|
|
57
|
+
requests.delete(controller);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
const publish = (event, data) => {
|
|
61
|
+
const chunk = encoder.encode(`event: ${event}\ndata: ${JSON.stringify(data)}\n\n`);
|
|
62
|
+
for (const controller of clients)
|
|
63
|
+
send(controller, chunk);
|
|
64
|
+
};
|
|
65
|
+
const offSaved = rt.onSaved(() => {
|
|
66
|
+
if (clients.size > 0)
|
|
67
|
+
publish("revision", { revision: state.revision });
|
|
68
|
+
});
|
|
69
|
+
let liveTimer;
|
|
70
|
+
const live = () => {
|
|
71
|
+
if (clients.size === 0 || liveTimer !== undefined)
|
|
72
|
+
return;
|
|
73
|
+
liveTimer = setTimeout(() => {
|
|
74
|
+
liveTimer = undefined;
|
|
75
|
+
publish("live", {});
|
|
76
|
+
}, LIVE_DELAY_MS);
|
|
77
|
+
};
|
|
78
|
+
listenGlobal(ctx, "agent/status", ({ agent }) => {
|
|
79
|
+
if (rt.selfOf(agent.id) !== undefined)
|
|
80
|
+
live();
|
|
81
|
+
});
|
|
82
|
+
listenGlobal(ctx, "session/event", (session, event) => {
|
|
83
|
+
if (clients.size === 0)
|
|
84
|
+
return;
|
|
85
|
+
const botId = rt.chatOwners.get(session.id);
|
|
86
|
+
if (botId === undefined)
|
|
87
|
+
return;
|
|
88
|
+
const displayItems = rt.displayItems;
|
|
89
|
+
if (typeof displayItems === "function") {
|
|
90
|
+
for (const item of displayItems(session.id, [event]))
|
|
91
|
+
publish("message", { botId, sessionId: session.id, item });
|
|
92
|
+
}
|
|
93
|
+
if (event.type === "tool/call")
|
|
94
|
+
live();
|
|
95
|
+
});
|
|
96
|
+
// A turn that answers another Bot is hidden; its frames never start a stream.
|
|
97
|
+
const hiddenTurn = (sessionId, turn) => turn !== undefined && (state.exchangeTurns[sessionId] ?? []).includes(turn);
|
|
98
|
+
listenGlobal(ctx, "agent/assistant-stream", ({ agent, frame }) => {
|
|
99
|
+
if (frame.type === "start") {
|
|
100
|
+
const botId = rt.chatOwners.get(agent.id);
|
|
101
|
+
if (clients.size === 0 || botId === undefined || hiddenTurn(agent.id, frame.turn))
|
|
102
|
+
return;
|
|
103
|
+
writing.set(frame.attemptId, { botId, sessionId: agent.id });
|
|
104
|
+
publish("stream", { botId, sessionId: agent.id, attemptId: frame.attemptId, phase: "start" });
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const target = writing.get(frame.attemptId);
|
|
108
|
+
if (target === undefined)
|
|
109
|
+
return;
|
|
110
|
+
if (frame.type === "chunk" && frame.chunk?.type === "text-delta")
|
|
111
|
+
publish("stream", { ...target, attemptId: frame.attemptId, text: frame.chunk.text });
|
|
112
|
+
else if (frame.type === "end") {
|
|
113
|
+
writing.delete(frame.attemptId);
|
|
114
|
+
publish("stream", { ...target, attemptId: frame.attemptId, phase: "end" });
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
ctx.effect(() => {
|
|
118
|
+
const connection = ctx.connection;
|
|
119
|
+
if (connection === undefined) {
|
|
120
|
+
rt.warn("ocean-crew: no connection service, so the live feed has no route");
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
return connection.fetch.register({
|
|
124
|
+
path: FEED_ROUTE,
|
|
125
|
+
methods: ["GET"],
|
|
126
|
+
requestBody: "buffered",
|
|
127
|
+
fetch: async (request) => {
|
|
128
|
+
let controller;
|
|
129
|
+
const body = new ReadableStream({
|
|
130
|
+
start(own) {
|
|
131
|
+
controller = own;
|
|
132
|
+
clients.add(own);
|
|
133
|
+
send(own, encoder.encode(`event: hello\ndata: ${JSON.stringify({ revision: state.revision })}\n\n`));
|
|
134
|
+
},
|
|
135
|
+
cancel() {
|
|
136
|
+
leave(controller);
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
// `start` runs synchronously in the constructor above, so it has run here.
|
|
140
|
+
const open = controller;
|
|
141
|
+
requests.set(open, request);
|
|
142
|
+
request.signal.addEventListener("abort", () => leave(open), { once: true });
|
|
143
|
+
return new Response(body, {
|
|
144
|
+
status: 200,
|
|
145
|
+
headers: { "content-type": "text/event-stream; charset=utf-8", "cache-control": "no-store" },
|
|
146
|
+
});
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
}, "ocean-crew: live feed route");
|
|
150
|
+
ctx.effect(() => {
|
|
151
|
+
const keepalive = setInterval(() => {
|
|
152
|
+
if (clients.size > 0)
|
|
153
|
+
for (const controller of clients)
|
|
154
|
+
send(controller, encoder.encode(": keepalive\n\n"));
|
|
155
|
+
}, KEEPALIVE_MS);
|
|
156
|
+
keepalive.unref?.();
|
|
157
|
+
return () => {
|
|
158
|
+
clearInterval(keepalive);
|
|
159
|
+
clearTimeout(liveTimer);
|
|
160
|
+
offSaved();
|
|
161
|
+
for (const controller of clients)
|
|
162
|
+
leave(controller);
|
|
163
|
+
};
|
|
164
|
+
}, "ocean-crew: live feed");
|
|
165
|
+
}
|