eve-hub 0.6.42 → 0.6.43
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/CHANGELOG.md +6 -0
- package/execution-context.mjs +2 -4
- package/kit-modules.json +1 -0
- package/memory/team.d.ts +20 -0
- package/memory/team.js +38 -0
- package/package.json +1 -1
- package/prepare.mjs +3 -4
- package/stubs.d.mts +1 -0
- package/stubs.mjs +7 -4
- package/sync-kit.mjs +1 -1
- package/team.d.mts +24 -0
- package/team.mjs +113 -0
- package/tools/list_agents.d.ts +6 -0
- package/tools/list_agents.js +1 -1
- package/workspace.mjs +4 -1
- package/world.mjs +383 -382
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# eve-hub
|
|
2
2
|
|
|
3
|
+
## 0.6.43
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Every bot is given the team model (who is on the team, where each runs, each bot's real tools from its eve runtime, who hands work to whom) as a memory slot that re-sends only when it changes, hosted bots included; `list_agents` is on every bot and reports each bot's kind, place and tools.
|
|
8
|
+
|
|
3
9
|
## 0.6.42
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
package/execution-context.mjs
CHANGED
|
@@ -39,15 +39,13 @@ export function deploymentFacts(env = process.env) {
|
|
|
39
39
|
|
|
40
40
|
const list = (items) => items.map((item) => `\`${item}\``).join(", ");
|
|
41
41
|
|
|
42
|
-
export function hostedContext({ bot,
|
|
43
|
-
const mac = roster.filter((agent) => agent.runsOn === "local").map((agent) => agent.name);
|
|
44
|
-
const cloud = roster.filter((agent) => agent.runsOn !== "local").map((agent) => agent.name);
|
|
42
|
+
export function hostedContext({ bot, kit, removed, deployment: d }, options) {
|
|
45
43
|
const dashboard = d.team && d.project ? `https://vercel.com/${d.team}/${d.project}` : undefined;
|
|
46
44
|
const lines = [
|
|
47
45
|
"## Execution context",
|
|
48
46
|
`You are \`${bot}\`, an eve agent deployed on Vercel, not on a laptop: \`hub.workspace.json\` defines you as hosted.`,
|
|
49
47
|
"",
|
|
50
|
-
|
|
48
|
+
"- **Workspace:** one hosted hub holds the threads and routes messages to each bot. Who is on the team, where each bot runs and what each can do come live from the hub: the team model message at the top of your turn (when it changes) and `list_agents`.",
|
|
51
49
|
`- **Package:** your tools, hooks, channel and instructions come from the \`eve-hub\` package, version ${kit}.`,
|
|
52
50
|
`- **Deployment:** ${[
|
|
53
51
|
(d.project ?? d.projectId) && `project \`${d.project ?? d.projectId}\``,
|
package/kit-modules.json
CHANGED
package/memory/team.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
declare const recall: ({ messages }: {
|
|
2
|
+
messages: readonly unknown[];
|
|
3
|
+
}) => Promise<{
|
|
4
|
+
messages: {
|
|
5
|
+
id: string;
|
|
6
|
+
content: string;
|
|
7
|
+
}[];
|
|
8
|
+
}>;
|
|
9
|
+
declare const _default: import("eve/memory").DefinedMemory<{
|
|
10
|
+
readonly description: "The team: who is on it, what each bot can do, and who hands work to whom.";
|
|
11
|
+
readonly namespace: "hub-team-memory";
|
|
12
|
+
readonly scope: "workspace";
|
|
13
|
+
readonly provider: {
|
|
14
|
+
readonly recall: {
|
|
15
|
+
"turn.started": typeof recall;
|
|
16
|
+
"compaction.completed": typeof recall;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
}>;
|
|
20
|
+
export default _default;
|
package/memory/team.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { defineMemory, defineMemoryProvider } from "eve/memory";
|
|
2
|
+
import { registryFetch } from "../registry-fetch.mjs";
|
|
3
|
+
import { teamRecall } from "../team.mjs";
|
|
4
|
+
// The team model (team.mjs) as an eve memory slot: roster, each bot's tools
|
|
5
|
+
// and the handoff rules, from the hub's GET /api/team. Recall runs at the start
|
|
6
|
+
// of every turn and after compaction, and adds one message only when the team
|
|
7
|
+
// differs from what this session was last given, so a turn where nothing
|
|
8
|
+
// changed carries nothing. A hub that can't answer within 5 s fails the recall, not the turn.
|
|
9
|
+
const warned = new Set();
|
|
10
|
+
async function fetchTeam(since) {
|
|
11
|
+
const res = await registryFetch(`/api/team${since ? `?since=${since}` : ""}`, { signal: AbortSignal.timeout(5_000) });
|
|
12
|
+
if (res.status === 304)
|
|
13
|
+
return null;
|
|
14
|
+
if (!res.ok)
|
|
15
|
+
throw new Error(`GET /api/team: HTTP ${res.status}: ${(await res.text()).slice(0, 200)}`);
|
|
16
|
+
return (await res.json());
|
|
17
|
+
}
|
|
18
|
+
const recall = async ({ messages }) => {
|
|
19
|
+
try {
|
|
20
|
+
return await teamRecall(messages, fetchTeam);
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
const note = error.message;
|
|
24
|
+
if (!warned.has(note)) {
|
|
25
|
+
warned.add(note);
|
|
26
|
+
console.warn(`team: ${note}`);
|
|
27
|
+
}
|
|
28
|
+
return { messages: [] };
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
export default defineMemory({
|
|
32
|
+
description: "The team: who is on it, what each bot can do, and who hands work to whom.",
|
|
33
|
+
namespace: "hub-team-memory",
|
|
34
|
+
scope: "workspace",
|
|
35
|
+
provider: defineMemoryProvider({
|
|
36
|
+
recall: { "turn.started": recall, "compaction.completed": recall },
|
|
37
|
+
}),
|
|
38
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eve-hub",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.43",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "Bot-side code shared by every hub agent: registry tools, the roster hook, the eve channel, the host sandbox and browser, and model helpers.",
|
|
6
6
|
"type": "module",
|
package/prepare.mjs
CHANGED
|
@@ -101,10 +101,10 @@ export default defineMemory({ namespace: ${JSON.stringify(`hub-${slot}-memory`)}
|
|
|
101
101
|
// Replaces the kit's execution-context stub (the local page) with the hosted
|
|
102
102
|
// page, for a bot the definition says is hosted. Its removed-tools list is read
|
|
103
103
|
// from the bot's folder as pruned.
|
|
104
|
-
function writeExecutionContext(bot, { bot: name,
|
|
104
|
+
function writeExecutionContext(bot, { bot: name, deployment }) {
|
|
105
105
|
const kitTools = Object.keys(name === "main" ? MAIN_KIT_FILES : KIT_FILES).filter((file) => file.startsWith("tools/") && file !== "tools/bash.ts").map((file) => basename(file, ".ts"));
|
|
106
106
|
const removed = kitTools.filter((tool) => !existsSync(join(bot, "tools", `${tool}.ts`)));
|
|
107
|
-
const section = hostedContext({ bot: name,
|
|
107
|
+
const section = hostedContext({ bot: name, kit: KIT_VERSION, removed, deployment });
|
|
108
108
|
mkdirSync(join(bot, "instructions"), { recursive: true });
|
|
109
109
|
writeFileSync(join(bot, "instructions", "execution-context.ts"), `import { defineInstructions } from "eve/instructions";\n\nexport default defineInstructions({ content: ${JSON.stringify(section)} });\n`);
|
|
110
110
|
}
|
|
@@ -141,11 +141,10 @@ export function prepareDeployment(dir) {
|
|
|
141
141
|
writeFileSync(join(bot, stub), content);
|
|
142
142
|
}
|
|
143
143
|
}
|
|
144
|
-
const roster = resolved.agents;
|
|
145
144
|
const deployment = deploymentFacts();
|
|
146
145
|
for (const name of readdirSync(join(dir, "agents"))) {
|
|
147
146
|
const bot = join(dir, "agents", name, "agent");
|
|
148
|
-
writeExecutionContext(bot, { bot: name,
|
|
147
|
+
writeExecutionContext(bot, { bot: name, deployment });
|
|
149
148
|
// The kit's extension (kit__worker) runs host-only workers and is excluded from the kit tarball.
|
|
150
149
|
// Other extensions, such as eve-code, stay.
|
|
151
150
|
for (const item of readdirSync(bot, { recursive: true })) {
|
package/stubs.d.mts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export const KIT_FILES: Record<string, string[]>;
|
|
2
2
|
export const MAIN_KIT_FILES: Record<string, string[]>;
|
|
3
3
|
export const DEPLOYED_KIT_FILES: Set<string>;
|
|
4
|
+
export const MEMORY_STUBS: Record<string, string>;
|
|
4
5
|
export const PROTOCOL_STUB_FILE: string;
|
|
5
6
|
export const PROTOCOL_STUB: string;
|
|
6
7
|
export function kitStub(file: string, options?: { files?: Record<string, string[]> }): string;
|
package/stubs.mjs
CHANGED
|
@@ -14,6 +14,7 @@ export const KIT_FILES = {
|
|
|
14
14
|
"tools/update_thread.ts": ["Update a thread this bot is in: title, track, domain and summary."],
|
|
15
15
|
"tools/list_threads.ts": ["Every workspace thread, from any session."],
|
|
16
16
|
"tools/read_thread.ts": ["Read any workspace thread."],
|
|
17
|
+
"tools/list_agents.ts": ["The team right now: kind, place, tools, models, who's busy."],
|
|
17
18
|
"tools/post_message.ts": ["Post into an existing thread or a bot's DM; with `status`, report the thread's phase."],
|
|
18
19
|
};
|
|
19
20
|
|
|
@@ -28,7 +29,6 @@ export const MAIN_KIT_FILES = {
|
|
|
28
29
|
"tools/move_path.ts": ["Rename or merge a path (project/track): every thread under it moves."],
|
|
29
30
|
"tools/archive_thread.ts": ["Archive a thread whose work is done."],
|
|
30
31
|
"tools/unarchive_thread.ts": ["Bring an archived thread back."],
|
|
31
|
-
"tools/list_agents.ts": ["The team right now: roles, models, who's busy."],
|
|
32
32
|
"tools/create_agent.ts": ["Add a bot to the workspace."],
|
|
33
33
|
"tools/remove_agent.ts": ["Remove a bot from the workspace."],
|
|
34
34
|
"tools/restart_agent.ts": ["Restart one bot's server."],
|
|
@@ -61,6 +61,7 @@ export const DEPLOYED_KIT_FILES = new Set([
|
|
|
61
61
|
"skills/onboarding-profile.ts",
|
|
62
62
|
"tools/onboarding_scripts.ts",
|
|
63
63
|
...["react", "show", "update_thread", "list_threads", "read_thread", "post_message"].map((t) => `tools/${t}.ts`),
|
|
64
|
+
"memory/team.ts",
|
|
64
65
|
...["create_thread", "set_summary", "move_path", "archive_thread", "unarchive_thread", "list_agents", "agent_send", "create_agent", "remove_agent", "restart_agent", "tag_threads", "dash_update", "apply_tags"].map((t) => `tools/${t}.ts`),
|
|
65
66
|
]);
|
|
66
67
|
|
|
@@ -97,9 +98,10 @@ import workspace from "../../../../hub.workspace.json" with { type: "json" };
|
|
|
97
98
|
export default defineInstructions({ content: executionContext(${JSON.stringify(name)}, workspace) });
|
|
98
99
|
`;
|
|
99
100
|
|
|
100
|
-
// Every bot (main too) has
|
|
101
|
-
// workspace's memory/ entries
|
|
102
|
-
//
|
|
101
|
+
// Every bot (main too) has three memory slots: memory/workspace.ts recalls the
|
|
102
|
+
// workspace's memory/ entries, memory/bot.ts the bot's own agents/<bot>/memory/
|
|
103
|
+
// entries, and memory/team.ts the team model from the hub, every turn
|
|
104
|
+
// (packages/agent-kit/memory/).
|
|
103
105
|
const memoryStub = (slot, what) => `// ${what}, recalled every turn. From eve-hub
|
|
104
106
|
// (packages/agent-kit/memory/${slot}.ts); add or supersede entries there, not here.
|
|
105
107
|
export { default } from "eve-hub/memory/${slot}";
|
|
@@ -107,6 +109,7 @@ export { default } from "eve-hub/memory/${slot}";
|
|
|
107
109
|
export const MEMORY_STUBS = {
|
|
108
110
|
"memory/workspace.ts": memoryStub("workspace", "The workspace's memory/ entries"),
|
|
109
111
|
"memory/bot.ts": memoryStub("bot", "This bot's own agents/<bot>/memory/ entries"),
|
|
112
|
+
"memory/team.ts": memoryStub("team", "The team model (roster, tools, handoffs) from the hub"),
|
|
110
113
|
};
|
|
111
114
|
|
|
112
115
|
// main's instructions aren't copied into a workspace: agent/instructions.ts
|
package/sync-kit.mjs
CHANGED
|
@@ -65,8 +65,8 @@ export function syncKit({ bots, modules }) {
|
|
|
65
65
|
const wanted = new Set(Object.keys(stubs).filter((file) => local || DEPLOYED_KIT_FILES.has(file)));
|
|
66
66
|
if (local) {
|
|
67
67
|
wanted.add(EXECUTION_CONTEXT_STUB_FILE);
|
|
68
|
-
for (const file of Object.keys(MEMORY_STUBS)) wanted.add(file);
|
|
69
68
|
}
|
|
69
|
+
for (const file of Object.keys(MEMORY_STUBS)) if (local || DEPLOYED_KIT_FILES.has(file)) wanted.add(file);
|
|
70
70
|
const content = (file) => (file === EXECUTION_CONTEXT_STUB_FILE ? executionContextStub(bot) : MEMORY_STUBS[file] ?? kitStub(file, { files: stubs }));
|
|
71
71
|
for (const file of Object.keys(files).sort()) {
|
|
72
72
|
const rel = file.replace(SUBAGENT, "");
|
package/team.d.mts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type TeamNode = {
|
|
2
|
+
id: string;
|
|
3
|
+
kind: "human" | "coordinator" | "planner" | "builder" | "reviewer" | "git-ops" | "demo" | "other";
|
|
4
|
+
label: string;
|
|
5
|
+
color?: string;
|
|
6
|
+
role?: string;
|
|
7
|
+
model?: string;
|
|
8
|
+
runsOn?: "hosted" | "local";
|
|
9
|
+
tools?: string[] | null;
|
|
10
|
+
subagents?: string[];
|
|
11
|
+
};
|
|
12
|
+
export type TeamEdge = { from: string; to: string; kind: string };
|
|
13
|
+
export type TeamModel = { version: 1; hash: string; nodes: TeamNode[]; edges: TeamEdge[] };
|
|
14
|
+
export type TeamAgent = { name: string; role: string; model: string; color?: string; kind?: string; runsOn?: "hosted" | "local"; subagents?: Record<string, unknown> };
|
|
15
|
+
export const KINDS: readonly string[];
|
|
16
|
+
export const MODEL_VERSION: 1;
|
|
17
|
+
export const NODE_KINDS: readonly [string, ...string[]];
|
|
18
|
+
export const EDGE_KINDS: readonly [string, ...string[]];
|
|
19
|
+
export const SECTIONS: readonly { id: string; edge: { kind: string; from: unknown; to: unknown }; text: string }[];
|
|
20
|
+
export const SECTIONS_HASH: string;
|
|
21
|
+
export function teamEdges(nodes: readonly TeamNode[]): TeamEdge[];
|
|
22
|
+
export function teamModel(input: { agents: readonly TeamAgent[]; humans?: readonly { name: string; color?: string }[]; tools?: Record<string, readonly string[] | null | undefined> }): TeamModel;
|
|
23
|
+
export function teamText(model: TeamModel): string;
|
|
24
|
+
export function teamRecall(history: readonly unknown[], fetchTeam: (since?: string) => Promise<TeamModel | null>): Promise<{ messages: { id: string; content: string }[] }>;
|
package/team.mjs
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// The team model: who is on the team, what each bot can do, and who hands work
|
|
2
|
+
// to whom. One derivation feeds every reader: the hub's GET /api/team (the
|
|
3
|
+
// native Team window, list_agents) and the prompt's team slot (memory/team.ts),
|
|
4
|
+
// which delivers it to a session once and again only when it changes.
|
|
5
|
+
//
|
|
6
|
+
// A bot's `kind` is its place in the team (hub.workspace.json). Its tools are
|
|
7
|
+
// what its running eve reports (/v1/info), never read from files. The edges
|
|
8
|
+
// come from SECTIONS: each protocol section names the edge it draws between
|
|
9
|
+
// kinds (or a tool), and the same entry renders the sentence the bots read.
|
|
10
|
+
import { createHash } from "node:crypto";
|
|
11
|
+
import { KINDS } from "./workspace.mjs";
|
|
12
|
+
|
|
13
|
+
export { KINDS };
|
|
14
|
+
export const MODEL_VERSION = 1;
|
|
15
|
+
export const NODE_KINDS = [...KINDS, "other", "human"];
|
|
16
|
+
|
|
17
|
+
const sha = (text, length) => createHash("sha256").update(text).digest("hex").slice(0, length);
|
|
18
|
+
|
|
19
|
+
// Selectors: "human", "bots" (every bot), { kind: [...] } or { tool: "agent_send" };
|
|
20
|
+
// all but the first pick bots. `text` is the sentence the team prompt carries when the section
|
|
21
|
+
// has at least one edge; `{from}` is the names behind the edge's from side.
|
|
22
|
+
export const SECTIONS = [
|
|
23
|
+
{ id: "partner", edge: { kind: "partner", from: "human", to: { kind: ["coordinator"] } }, text: "The coordinator works with the human directly." },
|
|
24
|
+
{ id: "route", edge: { kind: "route", from: { kind: ["coordinator"] }, to: { kind: ["planner", "builder"] } }, text: "The coordinator routes work to planners and builders." },
|
|
25
|
+
{ id: "plan", edge: { kind: "plan", from: { kind: ["planner"] }, to: { kind: ["builder"] } }, text: "Planners hand plans to builders." },
|
|
26
|
+
{ id: "build", edge: { kind: "build", from: { kind: ["builder"] }, to: { kind: ["reviewer"] } }, text: "Builders hand finished work to a reviewer." },
|
|
27
|
+
{ id: "fixfirst", edge: { kind: "fixfirst", from: { kind: ["reviewer"] }, to: { kind: ["builder"] } }, text: "Reviewers send fixes back to the builder that posted the build." },
|
|
28
|
+
{ id: "ship", edge: { kind: "ship", from: { kind: ["reviewer"] }, to: "human" }, text: "A reviewer's ship verdict goes to the human." },
|
|
29
|
+
{ id: "escalate", edge: { kind: "escalate", from: { kind: ["git-ops"] }, to: { kind: ["builder"] } }, text: "Git-ops bots report what they can't fix to a builder." },
|
|
30
|
+
{ id: "steer", edge: { kind: "steer", from: { tool: "agent_send" }, to: "bots" }, text: "Only {from} can queue work for or steer another bot (`agent_send`)." },
|
|
31
|
+
];
|
|
32
|
+
export const EDGE_KINDS = SECTIONS.map((section) => section.edge.kind);
|
|
33
|
+
export const SECTIONS_HASH = sha(JSON.stringify(SECTIONS), 8);
|
|
34
|
+
|
|
35
|
+
const pick = (selector, nodes) =>
|
|
36
|
+
nodes.filter((n) => {
|
|
37
|
+
if (selector === "human") return n.kind === "human";
|
|
38
|
+
if (n.kind === "human") return false;
|
|
39
|
+
return selector === "bots" || (selector.kind ? selector.kind.includes(n.kind) : Boolean(n.tools?.includes(selector.tool)));
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
export function teamEdges(nodes) {
|
|
43
|
+
const edges = [];
|
|
44
|
+
for (const { edge } of SECTIONS) {
|
|
45
|
+
const from = pick(edge.from, nodes);
|
|
46
|
+
const to = pick(edge.to, nodes);
|
|
47
|
+
for (const f of from) for (const t of to) if (f.id !== t.id) edges.push({ from: f.id, to: t.id, kind: edge.kind });
|
|
48
|
+
}
|
|
49
|
+
return edges;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// `agents`: hub.workspace.json's agents plus `runsOn` resolved; `tools`: bot
|
|
53
|
+
// name -> the tool names its eve reports, absent or null when not reported.
|
|
54
|
+
export function teamModel({ agents, humans = [], tools = {} }) {
|
|
55
|
+
const nodes = [
|
|
56
|
+
...humans.map((h) => ({ id: h.name, kind: "human", label: h.name, ...(h.color ? { color: h.color } : {}) })),
|
|
57
|
+
...agents.map((a) => ({
|
|
58
|
+
id: a.name,
|
|
59
|
+
kind: a.kind ?? "other",
|
|
60
|
+
label: a.name,
|
|
61
|
+
...(a.color ? { color: a.color } : {}),
|
|
62
|
+
role: a.role,
|
|
63
|
+
model: a.model,
|
|
64
|
+
runsOn: a.runsOn ?? "hosted",
|
|
65
|
+
tools: tools[a.name] ? [...new Set(tools[a.name])].sort() : null,
|
|
66
|
+
subagents: Object.keys(a.subagents ?? {}),
|
|
67
|
+
})),
|
|
68
|
+
];
|
|
69
|
+
const edges = teamEdges(nodes);
|
|
70
|
+
return { version: MODEL_VERSION, hash: sha(JSON.stringify({ nodes, edges }), 12), nodes, edges };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const list = (items) => items.join(", ");
|
|
74
|
+
|
|
75
|
+
// What a session reads: the roster with each bot's kind, place and tools, then
|
|
76
|
+
// the handoff sentences. Identical for every bot, so one slot text serves the
|
|
77
|
+
// whole team. The header carries both hashes: the model's and the sections'.
|
|
78
|
+
export function teamText(model) {
|
|
79
|
+
const bots = model.nodes.filter((n) => n.kind !== "human");
|
|
80
|
+
const reported = bots.filter((b) => b.tools);
|
|
81
|
+
const shared = reported.length ? reported[0].tools.filter((t) => reported.every((b) => b.tools.includes(t))) : [];
|
|
82
|
+
const lines = [`Team model ${model.hash}/${SECTIONS_HASH}: the team right now, from the hub. It replaces any earlier team model above.`, "", "Bots:"];
|
|
83
|
+
for (const b of bots) {
|
|
84
|
+
const own = b.tools?.filter((t) => !shared.includes(t));
|
|
85
|
+
const where = b.runsOn === "local" ? "on the owner's Mac" : "on Vercel";
|
|
86
|
+
lines.push(`- ${b.id} (${b.kind}, ${where}): ${b.role}${own?.length ? ` Also: ${list(own)}.` : ""}${b.tools ? "" : " Tools not reported."}${b.subagents.length ? ` Subagents: ${list(b.subagents)}.` : ""}`);
|
|
87
|
+
}
|
|
88
|
+
if (shared.length) lines.push("", `Every bot with reported tools has: ${list(shared)}.`);
|
|
89
|
+
const rules = SECTIONS.flatMap(({ id, edge, text }) => {
|
|
90
|
+
const from = model.edges.filter((e) => e.kind === edge.kind).map((e) => e.from);
|
|
91
|
+
return from.length ? [`- ${text.replace("{from}", list([...new Set(from)]))}`] : [];
|
|
92
|
+
});
|
|
93
|
+
if (rules.length) lines.push("", "Handoffs:", ...rules);
|
|
94
|
+
return lines.join("\n");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const textOf = (m) => (typeof m.content === "string" ? m.content : Array.isArray(m.content) ? m.content.map((p) => (p?.type === "text" ? p.text : "")).join("") : "");
|
|
98
|
+
const HEADER = /^Team model ([0-9a-f]{12})\/([0-9a-f]{8}):/;
|
|
99
|
+
|
|
100
|
+
// The team slot's recall, given the session's history: the team model as one
|
|
101
|
+
// message when it differs from the last one this session was given, nothing
|
|
102
|
+
// otherwise. `fetchTeam(since)` asks the hub, with the model hash the session
|
|
103
|
+
// holds (only while these sections are the ones that rendered it), and answers
|
|
104
|
+
// the model, or null when the hub says nothing changed.
|
|
105
|
+
export async function teamRecall(history, fetchTeam) {
|
|
106
|
+
const given = history.filter((m) => m?.role === "user" && m.kind === "memory.load" && HEADER.test(textOf(m))).map(textOf);
|
|
107
|
+
const last = HEADER.exec(given.at(-1) ?? "");
|
|
108
|
+
const model = await fetchTeam(last && last[2] === SECTIONS_HASH ? last[1] : undefined);
|
|
109
|
+
if (!model) return { messages: [] };
|
|
110
|
+
const text = teamText(model);
|
|
111
|
+
if (given.at(-1) === text) return { messages: [] };
|
|
112
|
+
return { messages: [{ id: `team#${given.length + 1}@${sha(text, 8)}`, content: text }] };
|
|
113
|
+
}
|
package/tools/list_agents.d.ts
CHANGED
|
@@ -2,6 +2,9 @@ declare const _default: import("eve/tools").ToolDefinition<Record<string, never>
|
|
|
2
2
|
name: string;
|
|
3
3
|
role: string;
|
|
4
4
|
model: string;
|
|
5
|
+
kind?: string;
|
|
6
|
+
runsOn?: "hosted" | "local";
|
|
7
|
+
tools?: string[] | null;
|
|
5
8
|
subagentCap: string;
|
|
6
9
|
subagents: Array<{
|
|
7
10
|
name: string;
|
|
@@ -31,6 +34,9 @@ declare const _default: import("eve/tools").ToolDefinition<Record<string, never>
|
|
|
31
34
|
name: string;
|
|
32
35
|
role: string;
|
|
33
36
|
model: string;
|
|
37
|
+
kind?: string;
|
|
38
|
+
runsOn?: "hosted" | "local";
|
|
39
|
+
tools?: string[] | null;
|
|
34
40
|
subagentCap: string;
|
|
35
41
|
subagents: Array<{
|
|
36
42
|
name: string;
|
package/tools/list_agents.js
CHANGED
|
@@ -2,7 +2,7 @@ import { defineTool } from "eve/tools";
|
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { registryFetch } from "../registry-fetch.mjs";
|
|
4
4
|
export default defineTool({
|
|
5
|
-
description: "List the workspace's bots right now: each one's role, model, the model and reasoning each of its declared subagents and its built-in `agent` copies resolve to under the workspace subagent cap (clamped from hub.workspace.json, never rejected), whether it's idle or busy (and where; a bot's threads run in parallel, so busy elsewhere doesn't mean it can't start work in a new thread), its server's memory (process.rssMb) and uptime (process.uptimeS; only on a local hub, since a hosted hub runs no bot process to measure), and any restart in progress (restart_agent, or the registry recycling a bot that grew too big or ran too long). The team changes; check it before routing work.",
|
|
5
|
+
description: "List the workspace's bots right now: each one's role, kind (its place in the team: coordinator, planner, builder, reviewer, git-ops, demo, or other), where it runs (hosted or local), the tools its runtime reports (null when not yet reported), its model, the model and reasoning each of its declared subagents and its built-in `agent` copies resolve to under the workspace subagent cap (clamped from hub.workspace.json, never rejected), whether it's idle or busy (and where; a bot's threads run in parallel, so busy elsewhere doesn't mean it can't start work in a new thread), its server's memory (process.rssMb) and uptime (process.uptimeS; only on a local hub, since a hosted hub runs no bot process to measure), and any restart in progress (restart_agent, or the registry recycling a bot that grew too big or ran too long). The team changes; check it before routing work.",
|
|
6
6
|
inputSchema: z.object({}),
|
|
7
7
|
async execute() {
|
|
8
8
|
// Anything but a 2xx list of bots is an error naming the status and the
|
package/workspace.mjs
CHANGED
|
@@ -47,9 +47,11 @@ export const DOMAIN = /^[a-z][a-z0-9-]*(\/[a-z][a-z0-9-]*)*$/;
|
|
|
47
47
|
export const NAME = /^[a-z][a-z0-9-]{1,30}$/;
|
|
48
48
|
export const HUMAN_NAME = /^[a-z][a-z0-9-]{0,30}$/;
|
|
49
49
|
const COLOR = /^#[0-9a-f]{3}([0-9a-f]{3})?$/i;
|
|
50
|
-
const AGENT_KEYS = new Set(["name", "model", "role", "color", "subagents", "subagentCap", "limits", "port", "runsOn", "domains"]);
|
|
50
|
+
const AGENT_KEYS = new Set(["name", "model", "role", "color", "subagents", "subagentCap", "limits", "port", "runsOn", "domains", "kind"]);
|
|
51
51
|
|
|
52
52
|
export const RUNS_ON = ["hosted", "local"];
|
|
53
|
+
// The bot's place in the team (team.mjs draws the handoffs between kinds).
|
|
54
|
+
export const KINDS = ["coordinator", "planner", "builder", "reviewer", "git-ops", "demo"];
|
|
53
55
|
|
|
54
56
|
export class WorkspaceError extends Error {}
|
|
55
57
|
|
|
@@ -102,6 +104,7 @@ export function parseWorkspace(data, file = WORKSPACE_FILE) {
|
|
|
102
104
|
// Where the bot runs: on the hosted bots project (default), or on the human's Mac, started by
|
|
103
105
|
// the app's local runner. Fixed at definition.
|
|
104
106
|
if (agent.runsOn !== undefined && !RUNS_ON.includes(agent.runsOn)) fail(file, `${label}.runsOn must be "hosted" or "local"`);
|
|
107
|
+
if (agent.kind !== undefined && !KINDS.includes(agent.kind)) fail(file, `${label}.kind must be one of ${KINDS.join(", ")}`);
|
|
105
108
|
if (agent.domains !== undefined && (!Array.isArray(agent.domains) || agent.domains.some((d) => typeof d !== "string" || !DOMAIN.test(d)))) fail(file, `${label}.domains must be an array of memory folders like "ui" or "ui/motion"`);
|
|
106
109
|
const unknown = Object.keys(agent).filter((k) => !AGENT_KEYS.has(k));
|
|
107
110
|
if (unknown.length) fail(file, `${label}: unknown field(s) ${unknown.join(", ")}`);
|
package/world.mjs
CHANGED
|
@@ -417,316 +417,6 @@ function reduceChild(member, { type, data = {}, meta: meta4 }, at = Date.parse(m
|
|
|
417
417
|
import { createClient } from "@libsql/client";
|
|
418
418
|
var s = (sql, ...args) => ({ sql, args });
|
|
419
419
|
|
|
420
|
-
// ../../hosted-hub/src/domain/ids.ts
|
|
421
|
-
import { createHash, randomBytes } from "node:crypto";
|
|
422
|
-
var alphabet = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
423
|
-
function base32(value) {
|
|
424
|
-
let out = "";
|
|
425
|
-
for (let i = 0; i < 26; i++) {
|
|
426
|
-
out = alphabet[Number(value & 31n)] + out;
|
|
427
|
-
value >>= 5n;
|
|
428
|
-
}
|
|
429
|
-
return out;
|
|
430
|
-
}
|
|
431
|
-
function ulid(now = Date.now()) {
|
|
432
|
-
return base32(BigInt(now) << 80n | BigInt(`0x${randomBytes(10).toString("hex")}`));
|
|
433
|
-
}
|
|
434
|
-
var eventId = () => `evt_${ulid()}`;
|
|
435
|
-
function derivedMessageId(...parts) {
|
|
436
|
-
return `msg_${base32(BigInt(`0x${createHash("sha256").update(parts.join("\0")).digest("hex").slice(0, 33)}`) >> 2n)}`;
|
|
437
|
-
}
|
|
438
|
-
var sessionId = () => `wrun_${ulid()}`;
|
|
439
|
-
var deliveryId = (message, bot) => `dlv_${message}_${bot}`;
|
|
440
|
-
var messageOfDelivery = (delivery, bot) => delivery.slice("dlv_".length, -`_${bot}`.length);
|
|
441
|
-
var turnId = (session, chunkIndex) => `trn_${session}_${chunkIndex}`;
|
|
442
|
-
|
|
443
|
-
// ../../hosted-hub/src/store/events.ts
|
|
444
|
-
function eventStatements(threadId2, event, text, at, id = eventId(), only) {
|
|
445
|
-
const expr = (v) => typeof v === "string" ? { sql: "?", args: [v] } : "sql" in v && "args" in v ? v : { sql: "?", args: [JSON.stringify(v)] };
|
|
446
|
-
const [t, e] = [expr(text), expr(event)];
|
|
447
|
-
return [
|
|
448
|
-
s(
|
|
449
|
-
`INSERT OR IGNORE INTO messages (id, thread_id, author, author_kind, text, created_at, event) SELECT ?, ?, 'system', 'event', ${t.sql}, ?, ${e.sql} WHERE ${only?.sql ?? "1"}`,
|
|
450
|
-
id,
|
|
451
|
-
threadId2,
|
|
452
|
-
...t.args,
|
|
453
|
-
at,
|
|
454
|
-
...e.args,
|
|
455
|
-
...only?.args ?? []
|
|
456
|
-
),
|
|
457
|
-
s(`INSERT INTO log (thread_id,type,data,at) SELECT ?, 'message.created', ?, ? WHERE changes() > 0`, threadId2, JSON.stringify({ threadId: threadId2, messageId: id, author: "system", authorKind: "event" }), at)
|
|
458
|
-
];
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
// ../../hosted-hub/src/store/routing.ts
|
|
462
|
-
var THREAD_REFS = 3;
|
|
463
|
-
var threadArgs = (threadId2, ...rest) => [...Array(THREAD_REFS).fill(threadId2), ...rest];
|
|
464
|
-
var PARTICIPANTS = (thread, except = "''") => `SELECT DISTINCT p.bot FROM (
|
|
465
|
-
SELECT thread_id AS tid, bot, created_at AS at, NULL AS mid FROM sessions WHERE thread_id = ${thread}
|
|
466
|
-
UNION ALL SELECT thread_id, author, created_at, id FROM messages WHERE thread_id = ${thread} AND author_kind = 'agent'
|
|
467
|
-
UNION ALL SELECT thread_id, json_extract(event, '$.agent'), created_at, id FROM messages WHERE thread_id = ${thread} AND author_kind = 'event' AND json_extract(event, '$.kind') = 'join') p
|
|
468
|
-
WHERE p.mid IS NOT ${except} AND p.at > COALESCE((SELECT MAX(l.created_at) FROM messages l WHERE l.thread_id = p.tid AND l.author_kind = 'event'
|
|
469
|
-
AND json_extract(l.event, '$.kind') = 'leave' AND json_extract(l.event, '$.agent') = p.bot), -1)`;
|
|
470
|
-
var hasJoins = (threadId2) => !threadId2.startsWith("dm-") && !threadId2.startsWith("sch-");
|
|
471
|
-
function routingStatements(messageId2, threadId2, bots, now, { by, joins }, held = {}, steer = false) {
|
|
472
|
-
const quiet2 = !joins || !hasJoins(threadId2);
|
|
473
|
-
const fresh = "EXISTS (SELECT 1 FROM messages WHERE id = ?)";
|
|
474
|
-
return [
|
|
475
|
-
...bots.flatMap((bot) => [
|
|
476
|
-
...quiet2 ? [] : eventStatements(
|
|
477
|
-
threadId2,
|
|
478
|
-
{ kind: "join", agent: bot, by, message: messageId2 },
|
|
479
|
-
`@${bot} joined${by !== bot ? ` (added by @${by})` : ""}`,
|
|
480
|
-
now,
|
|
481
|
-
eventId(),
|
|
482
|
-
s(`${fresh} AND NOT EXISTS (SELECT 1 FROM (${PARTICIPANTS("?")}) WHERE bot = ?)`, messageId2, ...threadArgs(threadId2), bot)
|
|
483
|
-
),
|
|
484
|
-
s(`INSERT OR IGNORE INTO sessions (id,thread_id,bot,created_at) SELECT ?,?,?,? WHERE ${fresh}`, sessionId(), threadId2, bot, now, messageId2),
|
|
485
|
-
s(
|
|
486
|
-
`INSERT OR IGNORE INTO deliveries (id,message_id,bot,session_id,created_at)
|
|
487
|
-
SELECT ?,?,?,id,? FROM sessions WHERE thread_id = ? AND bot = ? AND ended_at IS NULL AND ${fresh}`,
|
|
488
|
-
deliveryId(messageId2, bot),
|
|
489
|
-
messageId2,
|
|
490
|
-
bot,
|
|
491
|
-
now,
|
|
492
|
-
threadId2,
|
|
493
|
-
bot,
|
|
494
|
-
messageId2
|
|
495
|
-
),
|
|
496
|
-
...steer ? [s("UPDATE deliveries SET steer = 1 WHERE id = ? AND accepted_at IS NULL", deliveryId(messageId2, bot))] : [],
|
|
497
|
-
...held[bot] ? [s("UPDATE deliveries SET held_at = ?, held_scope = ? WHERE id = ? AND accepted_at IS NULL", now, held[bot], deliveryId(messageId2, bot))] : []
|
|
498
|
-
])
|
|
499
|
-
];
|
|
500
|
-
}
|
|
501
|
-
var hopAfter = (session) => s(`(SELECT 1 + COALESCE(MAX(m.hop), 0) FROM deliveries d JOIN messages m ON m.id = d.message_id WHERE d.session_id = ? AND (
|
|
502
|
-
EXISTS (SELECT 1 FROM turn_deliveries td WHERE td.delivery_id = d.id AND td.turn_id = (SELECT id FROM turns WHERE session_id = ? ORDER BY started_at DESC, rowid DESC LIMIT 1))
|
|
503
|
-
OR (d.accepted_at IS NOT NULL AND NOT EXISTS (SELECT 1 FROM turn_deliveries td WHERE td.delivery_id = d.id))))`, session, session);
|
|
504
|
-
|
|
505
|
-
// subagent-cap.mjs
|
|
506
|
-
var DEFAULT_SUBAGENT_CAP = Object.freeze({
|
|
507
|
-
model: "anthropic/claude-fable-5.1",
|
|
508
|
-
reasoning: "low",
|
|
509
|
-
models: Object.freeze(["anthropic/claude-fable-5.1"])
|
|
510
|
-
});
|
|
511
|
-
|
|
512
|
-
// ../../hosted-hub/src/store/roster.ts
|
|
513
|
-
var onLocal = (bot) => `EXISTS (SELECT 1 FROM bots WHERE name = ${bot} AND runs_on = 'local')`;
|
|
514
|
-
var HOP_LIMIT = "(SELECT json_extract(metadata, '$.workspace.hopLimit') FROM workspace_repo WHERE id = 1)";
|
|
515
|
-
var heldByHopLimit = (m) => `(${m}.author_kind = 'agent' AND ${HOP_LIMIT} IS NOT NULL AND ${m}.hop >= ${HOP_LIMIT})`;
|
|
516
|
-
|
|
517
|
-
// ../../hosted-hub/src/store/schema.ts
|
|
518
|
-
var OPEN_TURN_STATUSES = "('streaming','cancelling')";
|
|
519
|
-
|
|
520
|
-
// ../../hosted-hub/src/domain/wake.ts
|
|
521
|
-
var escape = (s2) => s2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
522
|
-
var mentioned = (roster, text) => roster.filter((bot) => new RegExp(`(^|[^\\w@])@${escape(bot)}(?![\\w-])`, "i").test(text));
|
|
523
|
-
var hasAll = (text) => /(^|[^\w@])@all(?![\w-])/i.test(text);
|
|
524
|
-
function recipients(roster, text, from, threadId2 = "", participants = []) {
|
|
525
|
-
if (threadId2.startsWith("dm-")) {
|
|
526
|
-
const bot = threadId2.slice(3);
|
|
527
|
-
return bot !== from.name && roster.includes(bot) ? [bot] : [];
|
|
528
|
-
}
|
|
529
|
-
const named = [.../* @__PURE__ */ new Set([...mentioned(roster, text), ...hasAll(text) ? participants.filter((b) => roster.includes(b)) : []])].filter((bot) => bot !== from.name);
|
|
530
|
-
if (from.kind === "agent") return named;
|
|
531
|
-
return named.length ? named : roster.slice(0, 1);
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
// ../../lib/usage.mjs
|
|
535
|
-
var emptyBucket = () => ({ steps: 0, inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, costUsd: null, unpricedSteps: 0 });
|
|
536
|
-
var TOKENS = ["inputTokens", "outputTokens", "cacheReadTokens", "cacheWriteTokens"];
|
|
537
|
-
var num = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0 ? v : 0;
|
|
538
|
-
var round = (v) => Math.round(v * 1e9) / 1e9;
|
|
539
|
-
function addUsage(bucket, usage = {}) {
|
|
540
|
-
bucket.steps += 1;
|
|
541
|
-
for (const key of TOKENS) bucket[key] += num(usage?.[key]);
|
|
542
|
-
const cost = usage?.costUsd;
|
|
543
|
-
if (typeof cost === "number" && Number.isFinite(cost) && cost >= 0) bucket.costUsd = round((bucket.costUsd ?? 0) + cost);
|
|
544
|
-
else bucket.unpricedSteps += 1;
|
|
545
|
-
return bucket;
|
|
546
|
-
}
|
|
547
|
-
var HOURS_KEPT_MS = 35 * 24 * 36e5;
|
|
548
|
-
|
|
549
|
-
// ../../hosted-hub/src/store/usage.ts
|
|
550
|
-
var STEP_INSERT = "INSERT OR IGNORE INTO usage_steps (session_id, sequence, step_index, at, input_tokens, output_tokens, cache_read_tokens, cache_write_tokens, cost_usd, turn_id, model)";
|
|
551
|
-
function stepValues(sessionId2, step) {
|
|
552
|
-
const b = addUsage(emptyBucket(), step.usage);
|
|
553
|
-
return [sessionId2, step.sequence, step.stepIndex, Date.parse(step.at), b.inputTokens, b.outputTokens, b.cacheReadTokens, b.cacheWriteTokens, b.costUsd];
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
// mention-gate.mjs
|
|
557
|
-
function codeRanges(text) {
|
|
558
|
-
const s2 = String(text ?? "");
|
|
559
|
-
const ranges = [];
|
|
560
|
-
const lines = [];
|
|
561
|
-
for (let at = 0; at <= s2.length; ) {
|
|
562
|
-
const nl = s2.indexOf("\n", at);
|
|
563
|
-
const end = nl < 0 ? s2.length : nl;
|
|
564
|
-
lines.push({ start: at, end, text: s2.slice(at, end) });
|
|
565
|
-
if (nl < 0) break;
|
|
566
|
-
at = nl + 1;
|
|
567
|
-
}
|
|
568
|
-
const fenced = /* @__PURE__ */ new Set();
|
|
569
|
-
for (let i = 0; i < lines.length; i++) {
|
|
570
|
-
const open3 = lines[i].text.match(/^ {0,3}(`{3,}|~{3,})(.*)$/);
|
|
571
|
-
if (!open3 || open3[1][0] === "`" && open3[2].includes("`")) continue;
|
|
572
|
-
const close = new RegExp(`^ {0,3}${open3[1][0] === "`" ? "`" : "~"}{${open3[1].length},}\\s*$`);
|
|
573
|
-
const j = lines.findIndex((l, k2) => k2 > i && close.test(l.text));
|
|
574
|
-
if (j < 0) continue;
|
|
575
|
-
ranges.push([lines[i].start, lines[j].end]);
|
|
576
|
-
for (let k2 = i; k2 <= j; k2++) fenced.add(k2);
|
|
577
|
-
i = j;
|
|
578
|
-
}
|
|
579
|
-
let para = [];
|
|
580
|
-
const flush = () => {
|
|
581
|
-
if (!para.length) return;
|
|
582
|
-
const from = para[0].start;
|
|
583
|
-
const t = s2.slice(from, para.at(-1).end);
|
|
584
|
-
const runs = [...t.matchAll(/`+/g)].map((m) => ({ at: m.index, n: m[0].length }));
|
|
585
|
-
for (let i = 0; i < runs.length; i++) {
|
|
586
|
-
let { at, n } = runs[i];
|
|
587
|
-
let slash = 0;
|
|
588
|
-
for (let b = at - 1; b >= 0 && t[b] === "\\"; b--) slash++;
|
|
589
|
-
if (slash % 2 === 1) {
|
|
590
|
-
at++;
|
|
591
|
-
n--;
|
|
592
|
-
if (!n) continue;
|
|
593
|
-
}
|
|
594
|
-
const j = runs.findIndex((r, k2) => k2 > i && r.n === n);
|
|
595
|
-
if (j < 0) continue;
|
|
596
|
-
ranges.push([from + at, from + runs[j].at + n]);
|
|
597
|
-
i = j;
|
|
598
|
-
}
|
|
599
|
-
para = [];
|
|
600
|
-
};
|
|
601
|
-
lines.forEach((l, i) => {
|
|
602
|
-
if (!fenced.has(i) && l.text.trim()) para.push(l);
|
|
603
|
-
flush();
|
|
604
|
-
});
|
|
605
|
-
return ranges;
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
// mention-tokens.mjs
|
|
609
|
-
function mentionTokens(text) {
|
|
610
|
-
const s2 = String(text ?? "");
|
|
611
|
-
const code = codeRanges(s2);
|
|
612
|
-
const out = [];
|
|
613
|
-
for (const match of s2.matchAll(/(?<![\w@./\\-])@([a-z0-9][a-z0-9-]*)/gi)) {
|
|
614
|
-
if (!code.some(([a, b]) => match.index >= a && match.index < b)) out.push(match[1].toLowerCase());
|
|
615
|
-
}
|
|
616
|
-
return out;
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
// ../../hosted-hub/src/store/notifications.ts
|
|
620
|
-
var NOTIFICATION_READS = s("SELECT person, notification_id FROM notification_reads");
|
|
621
|
-
var NOTIFICATIONS = s("SELECT message_id, person FROM notifications");
|
|
622
|
-
var names = (text, { person, names: names2 }) => {
|
|
623
|
-
const known = new Set([person, ...names2].map((n) => n.toLowerCase()));
|
|
624
|
-
return mentionTokens(text).some((t) => known.has(t));
|
|
625
|
-
};
|
|
626
|
-
var record = (id, person) => s("INSERT OR IGNORE INTO notifications (message_id, person) SELECT ?, ? WHERE EXISTS (SELECT 1 FROM messages WHERE id = ?)", id, person, id);
|
|
627
|
-
var notificationFor = (messageId2, kind, text, notify) => notify && kind === "agent" && names(text, notify) ? [record(messageId2, notify.person)] : [];
|
|
628
|
-
|
|
629
|
-
// ../../hosted-hub/src/dispatch/bot.ts
|
|
630
|
-
var MESSAGE_LINE = "\n[thread ";
|
|
631
|
-
|
|
632
|
-
// ../../hosted-hub/src/store/subagents.ts
|
|
633
|
-
var UP = `up(session_id, parent) AS (
|
|
634
|
-
SELECT session_id, parent FROM subagent_links WHERE session_id = ?
|
|
635
|
-
UNION SELECT c.session_id, c.parent FROM subagent_links c JOIN up ON c.session_id = up.parent)`;
|
|
636
|
-
var ROOT = "SELECT t.id, t.thread_id, t.bot FROM up JOIN turns t ON t.id = up.parent";
|
|
637
|
-
var subagentStep = (sessionId2, step) => s(`WITH RECURSIVE ${UP} ${STEP_INSERT} SELECT ?, ?, ?, ?, ?, ?, ?, ?, ?, (SELECT id FROM (${ROOT})), NULL`, sessionId2, ...stepValues(sessionId2, step));
|
|
638
|
-
var rootLog = (sessionId2, type, at) => s(`WITH RECURSIVE ${UP} INSERT INTO log (thread_id, type, data, at)
|
|
639
|
-
SELECT thread_id, ?, json_object('threadId', thread_id, 'agent', bot, 'turnId', id), ? FROM (${ROOT})`, sessionId2, type, at);
|
|
640
|
-
function linkStatements(sessionId2, parent, callId) {
|
|
641
|
-
const subtree = `down(session_id) AS (SELECT ? UNION SELECT c.session_id FROM subagent_links c JOIN down ON c.parent = down.session_id)`;
|
|
642
|
-
return [
|
|
643
|
-
s("INSERT OR IGNORE INTO subagent_links (session_id, parent, call_id) VALUES (?, ?, ?)", sessionId2, parent, callId),
|
|
644
|
-
s(`WITH RECURSIVE ${UP}, ${subtree} UPDATE usage_steps SET turn_id = (SELECT id FROM (${ROOT}))
|
|
645
|
-
WHERE turn_id IS NULL AND session_id IN (SELECT session_id FROM down) AND EXISTS (${ROOT})`, sessionId2, sessionId2)
|
|
646
|
-
];
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
// ../../hosted-hub/src/store/failures.ts
|
|
650
|
-
var STATE = "json_extract(event, '$.state')";
|
|
651
|
-
var failureId = (trigger, bot) => `evt_fail_${trigger}_${bot}`;
|
|
652
|
-
var cardLog = (idSql, now, ...args) => s(`INSERT INTO log (thread_id, type, data, at) SELECT thread_id, 'message.updated', json_object('threadId', thread_id, 'messageId', id), ? FROM messages WHERE id = (${idSql}) AND changes() > 0`, now, ...args);
|
|
653
|
-
function failureStatements(t, now) {
|
|
654
|
-
if (!t.trigger) return [];
|
|
655
|
-
const id = failureId(t.trigger, t.bot), detail = t.error ?? "unknown error";
|
|
656
|
-
const turn = (status) => s("EXISTS (SELECT 1 FROM turns WHERE id = ? AND status = ?)", t.id, status);
|
|
657
|
-
const failed = turn("failed"), settled = (status, next) => {
|
|
658
|
-
const when = turn(status);
|
|
659
|
-
return [s(`UPDATE messages SET event = json_set(event, '$.state', ?) WHERE id = ? AND ${STATE} = 'retrying' AND ${when.sql}`, next, id, ...when.args), cardLog("?", now, id)];
|
|
660
|
-
};
|
|
661
|
-
return [
|
|
662
|
-
...eventStatements(t.threadId, { kind: "turn_failed", agent: t.bot, triggerId: t.trigger, state: "failed", retries: 0, turnId: t.id, detail }, `${t.bot}'s turn failed: ${detail}`, now, id, failed),
|
|
663
|
-
s(`UPDATE messages SET text = ?, event = json_set(event, '$.state', 'failed', '$.detail', ?, '$.turnId', ?, '$.retries', json_extract(event, '$.retries') + 1)
|
|
664
|
-
WHERE id = ? AND ${STATE} = 'retrying' AND json_extract(event, '$.turnId') <> ? AND ${failed.sql}`, `${t.bot}'s turn failed: ${detail}`, detail, t.id, id, t.id, ...failed.args),
|
|
665
|
-
cardLog("?", now, id),
|
|
666
|
-
...settled("done", "resolved"),
|
|
667
|
-
...settled("cancelled", "failed")
|
|
668
|
-
];
|
|
669
|
-
}
|
|
670
|
-
var triggerOf = (t) => `(SELECT d.message_id FROM turn_deliveries td JOIN deliveries d ON d.id = td.delivery_id WHERE td.turn_id = ${t}.id ORDER BY d.rowid LIMIT 1)`;
|
|
671
|
-
|
|
672
|
-
// ../../lib/tags.mjs
|
|
673
|
-
var TAG_FIELDS = ["project", "issueType"];
|
|
674
|
-
var LABEL_FIELDS = [...TAG_FIELDS, "path"];
|
|
675
|
-
|
|
676
|
-
// ../../lib/subscriptions.mjs
|
|
677
|
-
var STATUSES = Object.freeze(["idle", "working", "waiting"]);
|
|
678
|
-
|
|
679
|
-
// ../../hosted-hub/src/store/dash.ts
|
|
680
|
-
var COLUMNS = { importance: "importance", importanceBy: "importance_by", lane: "lane", delay: "delay", reason: "reason", basis: "basis", deferred: "deferred", updatedAt: "classified_at", by: "classified_by" };
|
|
681
|
-
var FIELDS = Object.keys(COLUMNS);
|
|
682
|
-
var CLASSIFICATION_COLUMNS = FIELDS.map((f) => COLUMNS[f]).join(", ");
|
|
683
|
-
var DASH_READS = [s("SELECT * FROM dash_stamps")];
|
|
684
|
-
|
|
685
|
-
// ../../hosted-hub/src/store/app.ts
|
|
686
|
-
var APP_READS = [
|
|
687
|
-
s("SELECT * FROM thread_meta"),
|
|
688
|
-
s("SELECT * FROM read_states"),
|
|
689
|
-
s("SELECT * FROM read_marks"),
|
|
690
|
-
s("SELECT * FROM pins"),
|
|
691
|
-
s("SELECT * FROM reactions WHERE active = 1"),
|
|
692
|
-
s("SELECT * FROM human_profiles"),
|
|
693
|
-
...DASH_READS
|
|
694
|
-
];
|
|
695
|
-
|
|
696
|
-
// ../../hosted-hub/src/store/requests.ts
|
|
697
|
-
var SCHEDULE_LIMIT = `r.kind = 'session-limit' AND r.thread_id LIKE 'sch-%' AND NOT ${onLocal("r.bot")} AND r.sweep_gave_up_at IS NULL`;
|
|
698
|
-
var PENDING_REQUESTS_READ = s(`SELECT r.id, r.session_id, r.thread_id, r.bot, r.kind, r.prompt, r.options, r.allow_freeform, r.tool_name, r.submitted_at, r.created_at,
|
|
699
|
-
(SELECT d.message_id FROM turn_deliveries td JOIN deliveries d ON d.id = td.delivery_id WHERE td.turn_id = r.turn_id ORDER BY d.rowid DESC LIMIT 1) AS message_id
|
|
700
|
-
FROM input_requests r JOIN sessions s ON s.id = r.session_id
|
|
701
|
-
WHERE r.status = 'pending' AND s.ended_at IS NULL AND NOT (${SCHEDULE_LIMIT}) ORDER BY r.created_at`);
|
|
702
|
-
|
|
703
|
-
// ../../hosted-hub/src/store/workspace-state.ts
|
|
704
|
-
var LIVE_TURNS_READ = s(`SELECT thread_id,bot,started_at,reply_text,activity FROM turns WHERE status IN ${OPEN_TURN_STATUSES} ORDER BY started_at`);
|
|
705
|
-
|
|
706
|
-
// ../../hosted-hub/src/store/attachments.ts
|
|
707
|
-
var MAX_ATTACHMENTS = 10;
|
|
708
|
-
var OPEN_TURN = `(SELECT id FROM turns WHERE session_id = ? AND status IN ${OPEN_TURN_STATUSES} ORDER BY started_at DESC LIMIT 1)`;
|
|
709
|
-
var ATTACHMENTS_READ = s("SELECT message_id, upload_id, name FROM message_attachments ORDER BY message_id, position");
|
|
710
|
-
var unseenBy = (bot, thread, mid) => {
|
|
711
|
-
const end = `COALESCE((SELECT rowid FROM messages WHERE id = ${mid}), 9223372036854775807)`;
|
|
712
|
-
return `a.message_id = e.id AND e.thread_id = ${thread} AND e.author <> ${bot} AND e.rowid < ${end}
|
|
713
|
-
AND e.rowid > COALESCE((SELECT MAX(p.rowid) FROM deliveries dd JOIN messages p ON p.id = dd.message_id WHERE p.thread_id = e.thread_id AND dd.bot = ${bot} AND p.rowid < ${end}), 0)`;
|
|
714
|
-
};
|
|
715
|
-
|
|
716
|
-
// ../../hosted-hub/src/store/threads.ts
|
|
717
|
-
var SUMMARY_READ = s("SELECT scope,text,by,at FROM summaries");
|
|
718
|
-
|
|
719
|
-
// ../../lib/work-phases.mjs
|
|
720
|
-
var PHASES = Object.freeze(["planning", "working", "reviewing", "done"]);
|
|
721
|
-
var MOVES = Object.freeze({
|
|
722
|
-
"": ["planning", "working"],
|
|
723
|
-
planning: ["working"],
|
|
724
|
-
working: ["reviewing"],
|
|
725
|
-
reviewing: ["working", "planning", "done"],
|
|
726
|
-
done: []
|
|
727
|
-
});
|
|
728
|
-
var VERDICTS = Object.freeze(["ship", "fix-first", "rethink"]);
|
|
729
|
-
|
|
730
420
|
// ../../hosted-hub/node_modules/zod/v4/classic/external.js
|
|
731
421
|
var external_exports = {};
|
|
732
422
|
__export(external_exports, {
|
|
@@ -937,7 +627,7 @@ __export(external_exports, {
|
|
|
937
627
|
properties: () => _properties,
|
|
938
628
|
property: () => _property,
|
|
939
629
|
readonly: () => readonly,
|
|
940
|
-
record: () =>
|
|
630
|
+
record: () => record,
|
|
941
631
|
refine: () => refine,
|
|
942
632
|
regex: () => _regex,
|
|
943
633
|
regexes: () => regexes_exports,
|
|
@@ -971,7 +661,7 @@ __export(external_exports, {
|
|
|
971
661
|
tuple: () => tuple,
|
|
972
662
|
uint32: () => uint32,
|
|
973
663
|
uint64: () => uint64,
|
|
974
|
-
ulid: () =>
|
|
664
|
+
ulid: () => ulid2,
|
|
975
665
|
undefined: () => _undefined3,
|
|
976
666
|
union: () => union,
|
|
977
667
|
unknown: () => unknown,
|
|
@@ -2695,7 +2385,7 @@ __export(regexes_exports, {
|
|
|
2695
2385
|
sha512_hex: () => sha512_hex,
|
|
2696
2386
|
string: () => string,
|
|
2697
2387
|
time: () => time,
|
|
2698
|
-
ulid: () =>
|
|
2388
|
+
ulid: () => ulid,
|
|
2699
2389
|
undefined: () => _undefined,
|
|
2700
2390
|
unicodeEmail: () => unicodeEmail,
|
|
2701
2391
|
uppercase: () => uppercase,
|
|
@@ -2707,7 +2397,7 @@ __export(regexes_exports, {
|
|
|
2707
2397
|
});
|
|
2708
2398
|
var cuid = /^[cC][0-9a-z]{6,}$/;
|
|
2709
2399
|
var cuid2 = /^[0-9a-z]+$/;
|
|
2710
|
-
var
|
|
2400
|
+
var ulid = /^[0-7][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{25}$/;
|
|
2711
2401
|
var xid = /^[0-9a-vA-V]{20}$/;
|
|
2712
2402
|
var ksuid = /^[A-Za-z0-9]{27}$/;
|
|
2713
2403
|
var nanoid = /^[a-zA-Z0-9_-]{21}$/;
|
|
@@ -3646,7 +3336,7 @@ var $ZodCUID2 = /* @__PURE__ */ $constructor("$ZodCUID2", (inst, def) => {
|
|
|
3646
3336
|
$ZodStringFormat.init(inst, def);
|
|
3647
3337
|
});
|
|
3648
3338
|
var $ZodULID = /* @__PURE__ */ $constructor("$ZodULID", (inst, def) => {
|
|
3649
|
-
def.pattern ?? (def.pattern =
|
|
3339
|
+
def.pattern ?? (def.pattern = ulid);
|
|
3650
3340
|
$ZodStringFormat.init(inst, def);
|
|
3651
3341
|
});
|
|
3652
3342
|
var $ZodXID = /* @__PURE__ */ $constructor("$ZodXID", (inst, def) => {
|
|
@@ -5782,7 +5472,7 @@ var $ZodCyclicError = class extends Error {
|
|
|
5782
5472
|
this.name = "ZodCyclicError";
|
|
5783
5473
|
}
|
|
5784
5474
|
};
|
|
5785
|
-
var
|
|
5475
|
+
var STATE = "~memo";
|
|
5786
5476
|
var NO_ISSUES = [];
|
|
5787
5477
|
function isRef(value) {
|
|
5788
5478
|
return value !== null && typeof value === "object";
|
|
@@ -5996,10 +5686,10 @@ var memo = {
|
|
|
5996
5686
|
const input2 = payload.value;
|
|
5997
5687
|
if (!isRef(input2))
|
|
5998
5688
|
return base(payload, ctx);
|
|
5999
|
-
let state = ctx[
|
|
5689
|
+
let state = ctx[STATE];
|
|
6000
5690
|
if (!state) {
|
|
6001
5691
|
state = { buckets: /* @__PURE__ */ new WeakMap(), backEdges: void 0 };
|
|
6002
|
-
ctx[
|
|
5692
|
+
ctx[STATE] = state;
|
|
6003
5693
|
}
|
|
6004
5694
|
let bucket;
|
|
6005
5695
|
if (lastCtx === ctx) {
|
|
@@ -6048,7 +5738,7 @@ function memoizer() {
|
|
|
6048
5738
|
return memo;
|
|
6049
5739
|
}
|
|
6050
5740
|
function isBackEdge(ctx, value) {
|
|
6051
|
-
const backEdges = ctx[
|
|
5741
|
+
const backEdges = ctx[STATE]?.backEdges;
|
|
6052
5742
|
return backEdges !== void 0 && isRef(value) && backEdges.has(value);
|
|
6053
5743
|
}
|
|
6054
5744
|
|
|
@@ -17849,7 +17539,7 @@ __export(schemas_exports2, {
|
|
|
17849
17539
|
preprocess: () => preprocess,
|
|
17850
17540
|
promise: () => promise,
|
|
17851
17541
|
readonly: () => readonly,
|
|
17852
|
-
record: () =>
|
|
17542
|
+
record: () => record,
|
|
17853
17543
|
refine: () => refine,
|
|
17854
17544
|
set: () => set,
|
|
17855
17545
|
strictObject: () => strictObject,
|
|
@@ -17864,7 +17554,7 @@ __export(schemas_exports2, {
|
|
|
17864
17554
|
tuple: () => tuple,
|
|
17865
17555
|
uint32: () => uint32,
|
|
17866
17556
|
uint64: () => uint64,
|
|
17867
|
-
ulid: () =>
|
|
17557
|
+
ulid: () => ulid2,
|
|
17868
17558
|
undefined: () => _undefined3,
|
|
17869
17559
|
union: () => union,
|
|
17870
17560
|
unknown: () => unknown,
|
|
@@ -18398,7 +18088,7 @@ var ZodULID = /* @__PURE__ */ $constructor("ZodULID", (inst, def) => {
|
|
|
18398
18088
|
$ZodULID.init(inst, def);
|
|
18399
18089
|
ZodStringFormat.init(inst, def);
|
|
18400
18090
|
});
|
|
18401
|
-
function
|
|
18091
|
+
function ulid2(params) {
|
|
18402
18092
|
return _ulid(ZodULID, params);
|
|
18403
18093
|
}
|
|
18404
18094
|
var ZodXID = /* @__PURE__ */ $constructor("ZodXID", (inst, def) => {
|
|
@@ -18951,7 +18641,7 @@ var ZodRecord = /* @__PURE__ */ $constructor("ZodRecord", (inst, def) => {
|
|
|
18951
18641
|
inst.keyType = def.keyType;
|
|
18952
18642
|
inst.valueType = def.valueType;
|
|
18953
18643
|
});
|
|
18954
|
-
function
|
|
18644
|
+
function record(keyType, valueType, params) {
|
|
18955
18645
|
if (!valueType || !valueType._zod) {
|
|
18956
18646
|
return new ZodRecord({
|
|
18957
18647
|
type: "record",
|
|
@@ -19426,7 +19116,7 @@ var stringbool = (...args) => _stringbool({
|
|
|
19426
19116
|
}, ...args);
|
|
19427
19117
|
function json(params) {
|
|
19428
19118
|
const jsonSchema = lazy(() => {
|
|
19429
|
-
return union([string2(params), number2(), boolean2(), _null3(), array(jsonSchema),
|
|
19119
|
+
return union([string2(params), number2(), boolean2(), _null3(), array(jsonSchema), record(string2(), jsonSchema)]);
|
|
19430
19120
|
});
|
|
19431
19121
|
return jsonSchema;
|
|
19432
19122
|
}
|
|
@@ -20374,75 +20064,386 @@ function visit(schema, fnOrHandlers) {
|
|
|
20374
20064
|
}
|
|
20375
20065
|
}
|
|
20376
20066
|
}
|
|
20377
|
-
return run(schema);
|
|
20067
|
+
return run(schema);
|
|
20068
|
+
}
|
|
20069
|
+
|
|
20070
|
+
// ../../hosted-hub/node_modules/zod/v4/classic/deep-partial.js
|
|
20071
|
+
function deepPartial(schema) {
|
|
20072
|
+
return visit(schema, {
|
|
20073
|
+
object: (s2) => s2.partial(),
|
|
20074
|
+
// Every partialed option now admits `undefined`, which the constructor rejects as a duplicate.
|
|
20075
|
+
union: (s2) => {
|
|
20076
|
+
const def = s2._zod.def;
|
|
20077
|
+
return def.discriminator === void 0 ? s2 : union(def.options);
|
|
20078
|
+
}
|
|
20079
|
+
});
|
|
20080
|
+
}
|
|
20081
|
+
|
|
20082
|
+
// ../../hosted-hub/node_modules/zod/v4/classic/in-out.js
|
|
20083
|
+
function withChecks(side, checks) {
|
|
20084
|
+
if (!checks?.length)
|
|
20085
|
+
return side;
|
|
20086
|
+
const def = side._zod.def;
|
|
20087
|
+
return clone(side, mergeDefs(def, { checks: [...def.checks ?? [], ...checks] }), { parent: true });
|
|
20088
|
+
}
|
|
20089
|
+
function outSide(def) {
|
|
20090
|
+
return withChecks(def.out, def.checks);
|
|
20091
|
+
}
|
|
20092
|
+
function inSide(def) {
|
|
20093
|
+
return def.in._zod.traits.has("$ZodTransform") ? outSide(def) : def.in;
|
|
20094
|
+
}
|
|
20095
|
+
function input(schema) {
|
|
20096
|
+
return visit(schema, {
|
|
20097
|
+
pipe: (s2) => inSide(s2._zod.def),
|
|
20098
|
+
// A default value belongs to the output side, so a rewritten inner type leaves it stranded. `.default()` widens the declared input type with `undefined`, and `optional` is what carries that across.
|
|
20099
|
+
default: (s2, rewritten) => rewritten ? optional(s2._zod.def.innerType) : s2,
|
|
20100
|
+
// A catch value is output-side too, but `.catch()` leaves the declared input type alone, so the inner schema stands on its own.
|
|
20101
|
+
catch: (s2, rewritten) => rewritten ? s2._zod.def.innerType : s2
|
|
20102
|
+
});
|
|
20103
|
+
}
|
|
20104
|
+
function output(schema) {
|
|
20105
|
+
return visit(schema, {
|
|
20106
|
+
pipe: (s2) => outSide(s2._zod.def),
|
|
20107
|
+
// A prefault value is fed through the schema, which makes it input-side, so a rewritten inner type leaves it stranded.
|
|
20108
|
+
prefault: (s2, rewritten) => rewritten ? s2._zod.def.innerType : s2
|
|
20109
|
+
});
|
|
20110
|
+
}
|
|
20111
|
+
|
|
20112
|
+
// ../../hosted-hub/node_modules/zod/v4/classic/coerce.js
|
|
20113
|
+
var coerce_exports = {};
|
|
20114
|
+
__export(coerce_exports, {
|
|
20115
|
+
bigint: () => bigint3,
|
|
20116
|
+
boolean: () => boolean3,
|
|
20117
|
+
date: () => date4,
|
|
20118
|
+
number: () => number3,
|
|
20119
|
+
string: () => string3
|
|
20120
|
+
});
|
|
20121
|
+
function string3(params) {
|
|
20122
|
+
return _coercedString(ZodString, params);
|
|
20123
|
+
}
|
|
20124
|
+
function number3(params) {
|
|
20125
|
+
return _coercedNumber(ZodNumber, params);
|
|
20126
|
+
}
|
|
20127
|
+
function boolean3(params) {
|
|
20128
|
+
return _coercedBoolean(ZodBoolean, params);
|
|
20129
|
+
}
|
|
20130
|
+
function bigint3(params) {
|
|
20131
|
+
return _coercedBigint(ZodBigInt, params);
|
|
20132
|
+
}
|
|
20133
|
+
function date4(params) {
|
|
20134
|
+
return _coercedDate(ZodDate, params);
|
|
20135
|
+
}
|
|
20136
|
+
|
|
20137
|
+
// ../../hosted-hub/src/domain/ids.ts
|
|
20138
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
20139
|
+
var alphabet = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
20140
|
+
function base32(value) {
|
|
20141
|
+
let out = "";
|
|
20142
|
+
for (let i = 0; i < 26; i++) {
|
|
20143
|
+
out = alphabet[Number(value & 31n)] + out;
|
|
20144
|
+
value >>= 5n;
|
|
20145
|
+
}
|
|
20146
|
+
return out;
|
|
20147
|
+
}
|
|
20148
|
+
function ulid3(now = Date.now()) {
|
|
20149
|
+
return base32(BigInt(now) << 80n | BigInt(`0x${randomBytes(10).toString("hex")}`));
|
|
20150
|
+
}
|
|
20151
|
+
var eventId = () => `evt_${ulid3()}`;
|
|
20152
|
+
function derivedMessageId(...parts) {
|
|
20153
|
+
return `msg_${base32(BigInt(`0x${createHash("sha256").update(parts.join("\0")).digest("hex").slice(0, 33)}`) >> 2n)}`;
|
|
20154
|
+
}
|
|
20155
|
+
var sessionId = () => `wrun_${ulid3()}`;
|
|
20156
|
+
var deliveryId = (message, bot) => `dlv_${message}_${bot}`;
|
|
20157
|
+
var messageOfDelivery = (delivery, bot) => delivery.slice("dlv_".length, -`_${bot}`.length);
|
|
20158
|
+
var turnId = (session, chunkIndex) => `trn_${session}_${chunkIndex}`;
|
|
20159
|
+
|
|
20160
|
+
// ../../hosted-hub/src/store/events.ts
|
|
20161
|
+
function eventStatements(threadId2, event, text, at, id = eventId(), only) {
|
|
20162
|
+
const expr = (v) => typeof v === "string" ? { sql: "?", args: [v] } : "sql" in v && "args" in v ? v : { sql: "?", args: [JSON.stringify(v)] };
|
|
20163
|
+
const [t, e] = [expr(text), expr(event)];
|
|
20164
|
+
return [
|
|
20165
|
+
s(
|
|
20166
|
+
`INSERT OR IGNORE INTO messages (id, thread_id, author, author_kind, text, created_at, event) SELECT ?, ?, 'system', 'event', ${t.sql}, ?, ${e.sql} WHERE ${only?.sql ?? "1"}`,
|
|
20167
|
+
id,
|
|
20168
|
+
threadId2,
|
|
20169
|
+
...t.args,
|
|
20170
|
+
at,
|
|
20171
|
+
...e.args,
|
|
20172
|
+
...only?.args ?? []
|
|
20173
|
+
),
|
|
20174
|
+
s(`INSERT INTO log (thread_id,type,data,at) SELECT ?, 'message.created', ?, ? WHERE changes() > 0`, threadId2, JSON.stringify({ threadId: threadId2, messageId: id, author: "system", authorKind: "event" }), at)
|
|
20175
|
+
];
|
|
20176
|
+
}
|
|
20177
|
+
|
|
20178
|
+
// ../../hosted-hub/src/store/routing.ts
|
|
20179
|
+
var THREAD_REFS = 3;
|
|
20180
|
+
var threadArgs = (threadId2, ...rest) => [...Array(THREAD_REFS).fill(threadId2), ...rest];
|
|
20181
|
+
var PARTICIPANTS = (thread, except = "''") => `SELECT DISTINCT p.bot FROM (
|
|
20182
|
+
SELECT thread_id AS tid, bot, created_at AS at, NULL AS mid FROM sessions WHERE thread_id = ${thread}
|
|
20183
|
+
UNION ALL SELECT thread_id, author, created_at, id FROM messages WHERE thread_id = ${thread} AND author_kind = 'agent'
|
|
20184
|
+
UNION ALL SELECT thread_id, json_extract(event, '$.agent'), created_at, id FROM messages WHERE thread_id = ${thread} AND author_kind = 'event' AND json_extract(event, '$.kind') = 'join') p
|
|
20185
|
+
WHERE p.mid IS NOT ${except} AND p.at > COALESCE((SELECT MAX(l.created_at) FROM messages l WHERE l.thread_id = p.tid AND l.author_kind = 'event'
|
|
20186
|
+
AND json_extract(l.event, '$.kind') = 'leave' AND json_extract(l.event, '$.agent') = p.bot), -1)`;
|
|
20187
|
+
var hasJoins = (threadId2) => !threadId2.startsWith("dm-") && !threadId2.startsWith("sch-");
|
|
20188
|
+
function routingStatements(messageId2, threadId2, bots, now, { by, joins }, held = {}, steer = false) {
|
|
20189
|
+
const quiet2 = !joins || !hasJoins(threadId2);
|
|
20190
|
+
const fresh = "EXISTS (SELECT 1 FROM messages WHERE id = ?)";
|
|
20191
|
+
return [
|
|
20192
|
+
...bots.flatMap((bot) => [
|
|
20193
|
+
...quiet2 ? [] : eventStatements(
|
|
20194
|
+
threadId2,
|
|
20195
|
+
{ kind: "join", agent: bot, by, message: messageId2 },
|
|
20196
|
+
`@${bot} joined${by !== bot ? ` (added by @${by})` : ""}`,
|
|
20197
|
+
now,
|
|
20198
|
+
eventId(),
|
|
20199
|
+
s(`${fresh} AND NOT EXISTS (SELECT 1 FROM (${PARTICIPANTS("?")}) WHERE bot = ?)`, messageId2, ...threadArgs(threadId2), bot)
|
|
20200
|
+
),
|
|
20201
|
+
s(`INSERT OR IGNORE INTO sessions (id,thread_id,bot,created_at) SELECT ?,?,?,? WHERE ${fresh}`, sessionId(), threadId2, bot, now, messageId2),
|
|
20202
|
+
s(
|
|
20203
|
+
`INSERT OR IGNORE INTO deliveries (id,message_id,bot,session_id,created_at)
|
|
20204
|
+
SELECT ?,?,?,id,? FROM sessions WHERE thread_id = ? AND bot = ? AND ended_at IS NULL AND ${fresh}`,
|
|
20205
|
+
deliveryId(messageId2, bot),
|
|
20206
|
+
messageId2,
|
|
20207
|
+
bot,
|
|
20208
|
+
now,
|
|
20209
|
+
threadId2,
|
|
20210
|
+
bot,
|
|
20211
|
+
messageId2
|
|
20212
|
+
),
|
|
20213
|
+
...steer ? [s("UPDATE deliveries SET steer = 1 WHERE id = ? AND accepted_at IS NULL", deliveryId(messageId2, bot))] : [],
|
|
20214
|
+
...held[bot] ? [s("UPDATE deliveries SET held_at = ?, held_scope = ? WHERE id = ? AND accepted_at IS NULL", now, held[bot], deliveryId(messageId2, bot))] : []
|
|
20215
|
+
])
|
|
20216
|
+
];
|
|
20217
|
+
}
|
|
20218
|
+
var hopAfter = (session) => s(`(SELECT 1 + COALESCE(MAX(m.hop), 0) FROM deliveries d JOIN messages m ON m.id = d.message_id WHERE d.session_id = ? AND (
|
|
20219
|
+
EXISTS (SELECT 1 FROM turn_deliveries td WHERE td.delivery_id = d.id AND td.turn_id = (SELECT id FROM turns WHERE session_id = ? ORDER BY started_at DESC, rowid DESC LIMIT 1))
|
|
20220
|
+
OR (d.accepted_at IS NOT NULL AND NOT EXISTS (SELECT 1 FROM turn_deliveries td WHERE td.delivery_id = d.id))))`, session, session);
|
|
20221
|
+
|
|
20222
|
+
// subagent-cap.mjs
|
|
20223
|
+
var DEFAULT_SUBAGENT_CAP = Object.freeze({
|
|
20224
|
+
model: "anthropic/claude-fable-5.1",
|
|
20225
|
+
reasoning: "low",
|
|
20226
|
+
models: Object.freeze(["anthropic/claude-fable-5.1"])
|
|
20227
|
+
});
|
|
20228
|
+
|
|
20229
|
+
// ../../hosted-hub/src/store/roster.ts
|
|
20230
|
+
var onLocal = (bot) => `EXISTS (SELECT 1 FROM bots WHERE name = ${bot} AND runs_on = 'local')`;
|
|
20231
|
+
var HOP_LIMIT = "(SELECT json_extract(metadata, '$.workspace.hopLimit') FROM workspace_repo WHERE id = 1)";
|
|
20232
|
+
var heldByHopLimit = (m) => `(${m}.author_kind = 'agent' AND ${HOP_LIMIT} IS NOT NULL AND ${m}.hop >= ${HOP_LIMIT})`;
|
|
20233
|
+
var ToolInfo = external_exports.object({ tools: external_exports.object({ static: external_exports.array(external_exports.object({ name: external_exports.string().min(1) })) }) });
|
|
20234
|
+
|
|
20235
|
+
// ../../hosted-hub/src/store/schema.ts
|
|
20236
|
+
var OPEN_TURN_STATUSES = "('streaming','cancelling')";
|
|
20237
|
+
|
|
20238
|
+
// ../../hosted-hub/src/domain/wake.ts
|
|
20239
|
+
var escape = (s2) => s2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
20240
|
+
var mentioned = (roster, text) => roster.filter((bot) => new RegExp(`(^|[^\\w@])@${escape(bot)}(?![\\w-])`, "i").test(text));
|
|
20241
|
+
var hasAll = (text) => /(^|[^\w@])@all(?![\w-])/i.test(text);
|
|
20242
|
+
function recipients(roster, text, from, threadId2 = "", participants = []) {
|
|
20243
|
+
if (threadId2.startsWith("dm-")) {
|
|
20244
|
+
const bot = threadId2.slice(3);
|
|
20245
|
+
return bot !== from.name && roster.includes(bot) ? [bot] : [];
|
|
20246
|
+
}
|
|
20247
|
+
const named = [.../* @__PURE__ */ new Set([...mentioned(roster, text), ...hasAll(text) ? participants.filter((b) => roster.includes(b)) : []])].filter((bot) => bot !== from.name);
|
|
20248
|
+
if (from.kind === "agent") return named;
|
|
20249
|
+
return named.length ? named : roster.slice(0, 1);
|
|
20378
20250
|
}
|
|
20379
20251
|
|
|
20380
|
-
// ../../
|
|
20381
|
-
|
|
20382
|
-
|
|
20383
|
-
|
|
20384
|
-
|
|
20385
|
-
|
|
20386
|
-
|
|
20387
|
-
|
|
20388
|
-
|
|
20389
|
-
|
|
20252
|
+
// ../../lib/usage.mjs
|
|
20253
|
+
var emptyBucket = () => ({ steps: 0, inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, costUsd: null, unpricedSteps: 0 });
|
|
20254
|
+
var TOKENS = ["inputTokens", "outputTokens", "cacheReadTokens", "cacheWriteTokens"];
|
|
20255
|
+
var num = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0 ? v : 0;
|
|
20256
|
+
var round = (v) => Math.round(v * 1e9) / 1e9;
|
|
20257
|
+
function addUsage(bucket, usage = {}) {
|
|
20258
|
+
bucket.steps += 1;
|
|
20259
|
+
for (const key of TOKENS) bucket[key] += num(usage?.[key]);
|
|
20260
|
+
const cost = usage?.costUsd;
|
|
20261
|
+
if (typeof cost === "number" && Number.isFinite(cost) && cost >= 0) bucket.costUsd = round((bucket.costUsd ?? 0) + cost);
|
|
20262
|
+
else bucket.unpricedSteps += 1;
|
|
20263
|
+
return bucket;
|
|
20390
20264
|
}
|
|
20265
|
+
var HOURS_KEPT_MS = 35 * 24 * 36e5;
|
|
20391
20266
|
|
|
20392
|
-
// ../../hosted-hub/
|
|
20393
|
-
|
|
20394
|
-
|
|
20395
|
-
|
|
20396
|
-
|
|
20397
|
-
return clone(side, mergeDefs(def, { checks: [...def.checks ?? [], ...checks] }), { parent: true });
|
|
20398
|
-
}
|
|
20399
|
-
function outSide(def) {
|
|
20400
|
-
return withChecks(def.out, def.checks);
|
|
20401
|
-
}
|
|
20402
|
-
function inSide(def) {
|
|
20403
|
-
return def.in._zod.traits.has("$ZodTransform") ? outSide(def) : def.in;
|
|
20404
|
-
}
|
|
20405
|
-
function input(schema) {
|
|
20406
|
-
return visit(schema, {
|
|
20407
|
-
pipe: (s2) => inSide(s2._zod.def),
|
|
20408
|
-
// A default value belongs to the output side, so a rewritten inner type leaves it stranded. `.default()` widens the declared input type with `undefined`, and `optional` is what carries that across.
|
|
20409
|
-
default: (s2, rewritten) => rewritten ? optional(s2._zod.def.innerType) : s2,
|
|
20410
|
-
// A catch value is output-side too, but `.catch()` leaves the declared input type alone, so the inner schema stands on its own.
|
|
20411
|
-
catch: (s2, rewritten) => rewritten ? s2._zod.def.innerType : s2
|
|
20412
|
-
});
|
|
20267
|
+
// ../../hosted-hub/src/store/usage.ts
|
|
20268
|
+
var STEP_INSERT = "INSERT OR IGNORE INTO usage_steps (session_id, sequence, step_index, at, input_tokens, output_tokens, cache_read_tokens, cache_write_tokens, cost_usd, turn_id, model)";
|
|
20269
|
+
function stepValues(sessionId2, step) {
|
|
20270
|
+
const b = addUsage(emptyBucket(), step.usage);
|
|
20271
|
+
return [sessionId2, step.sequence, step.stepIndex, Date.parse(step.at), b.inputTokens, b.outputTokens, b.cacheReadTokens, b.cacheWriteTokens, b.costUsd];
|
|
20413
20272
|
}
|
|
20414
|
-
|
|
20415
|
-
|
|
20416
|
-
|
|
20417
|
-
|
|
20418
|
-
|
|
20273
|
+
|
|
20274
|
+
// mention-gate.mjs
|
|
20275
|
+
function codeRanges(text) {
|
|
20276
|
+
const s2 = String(text ?? "");
|
|
20277
|
+
const ranges = [];
|
|
20278
|
+
const lines = [];
|
|
20279
|
+
for (let at = 0; at <= s2.length; ) {
|
|
20280
|
+
const nl = s2.indexOf("\n", at);
|
|
20281
|
+
const end = nl < 0 ? s2.length : nl;
|
|
20282
|
+
lines.push({ start: at, end, text: s2.slice(at, end) });
|
|
20283
|
+
if (nl < 0) break;
|
|
20284
|
+
at = nl + 1;
|
|
20285
|
+
}
|
|
20286
|
+
const fenced = /* @__PURE__ */ new Set();
|
|
20287
|
+
for (let i = 0; i < lines.length; i++) {
|
|
20288
|
+
const open3 = lines[i].text.match(/^ {0,3}(`{3,}|~{3,})(.*)$/);
|
|
20289
|
+
if (!open3 || open3[1][0] === "`" && open3[2].includes("`")) continue;
|
|
20290
|
+
const close = new RegExp(`^ {0,3}${open3[1][0] === "`" ? "`" : "~"}{${open3[1].length},}\\s*$`);
|
|
20291
|
+
const j = lines.findIndex((l, k2) => k2 > i && close.test(l.text));
|
|
20292
|
+
if (j < 0) continue;
|
|
20293
|
+
ranges.push([lines[i].start, lines[j].end]);
|
|
20294
|
+
for (let k2 = i; k2 <= j; k2++) fenced.add(k2);
|
|
20295
|
+
i = j;
|
|
20296
|
+
}
|
|
20297
|
+
let para = [];
|
|
20298
|
+
const flush = () => {
|
|
20299
|
+
if (!para.length) return;
|
|
20300
|
+
const from = para[0].start;
|
|
20301
|
+
const t = s2.slice(from, para.at(-1).end);
|
|
20302
|
+
const runs = [...t.matchAll(/`+/g)].map((m) => ({ at: m.index, n: m[0].length }));
|
|
20303
|
+
for (let i = 0; i < runs.length; i++) {
|
|
20304
|
+
let { at, n } = runs[i];
|
|
20305
|
+
let slash = 0;
|
|
20306
|
+
for (let b = at - 1; b >= 0 && t[b] === "\\"; b--) slash++;
|
|
20307
|
+
if (slash % 2 === 1) {
|
|
20308
|
+
at++;
|
|
20309
|
+
n--;
|
|
20310
|
+
if (!n) continue;
|
|
20311
|
+
}
|
|
20312
|
+
const j = runs.findIndex((r, k2) => k2 > i && r.n === n);
|
|
20313
|
+
if (j < 0) continue;
|
|
20314
|
+
ranges.push([from + at, from + runs[j].at + n]);
|
|
20315
|
+
i = j;
|
|
20316
|
+
}
|
|
20317
|
+
para = [];
|
|
20318
|
+
};
|
|
20319
|
+
lines.forEach((l, i) => {
|
|
20320
|
+
if (!fenced.has(i) && l.text.trim()) para.push(l);
|
|
20321
|
+
flush();
|
|
20419
20322
|
});
|
|
20323
|
+
return ranges;
|
|
20420
20324
|
}
|
|
20421
20325
|
|
|
20422
|
-
//
|
|
20423
|
-
|
|
20424
|
-
|
|
20425
|
-
|
|
20426
|
-
|
|
20427
|
-
|
|
20428
|
-
|
|
20429
|
-
|
|
20430
|
-
|
|
20431
|
-
function string3(params) {
|
|
20432
|
-
return _coercedString(ZodString, params);
|
|
20433
|
-
}
|
|
20434
|
-
function number3(params) {
|
|
20435
|
-
return _coercedNumber(ZodNumber, params);
|
|
20436
|
-
}
|
|
20437
|
-
function boolean3(params) {
|
|
20438
|
-
return _coercedBoolean(ZodBoolean, params);
|
|
20326
|
+
// mention-tokens.mjs
|
|
20327
|
+
function mentionTokens(text) {
|
|
20328
|
+
const s2 = String(text ?? "");
|
|
20329
|
+
const code = codeRanges(s2);
|
|
20330
|
+
const out = [];
|
|
20331
|
+
for (const match of s2.matchAll(/(?<![\w@./\\-])@([a-z0-9][a-z0-9-]*)/gi)) {
|
|
20332
|
+
if (!code.some(([a, b]) => match.index >= a && match.index < b)) out.push(match[1].toLowerCase());
|
|
20333
|
+
}
|
|
20334
|
+
return out;
|
|
20439
20335
|
}
|
|
20440
|
-
|
|
20441
|
-
|
|
20336
|
+
|
|
20337
|
+
// ../../hosted-hub/src/store/notifications.ts
|
|
20338
|
+
var NOTIFICATION_READS = s("SELECT person, notification_id FROM notification_reads");
|
|
20339
|
+
var NOTIFICATIONS = s("SELECT message_id, person FROM notifications");
|
|
20340
|
+
var names = (text, { person, names: names2 }) => {
|
|
20341
|
+
const known = new Set([person, ...names2].map((n) => n.toLowerCase()));
|
|
20342
|
+
return mentionTokens(text).some((t) => known.has(t));
|
|
20343
|
+
};
|
|
20344
|
+
var record2 = (id, person) => s("INSERT OR IGNORE INTO notifications (message_id, person) SELECT ?, ? WHERE EXISTS (SELECT 1 FROM messages WHERE id = ?)", id, person, id);
|
|
20345
|
+
var notificationFor = (messageId2, kind, text, notify) => notify && kind === "agent" && names(text, notify) ? [record2(messageId2, notify.person)] : [];
|
|
20346
|
+
|
|
20347
|
+
// ../../hosted-hub/src/dispatch/bot.ts
|
|
20348
|
+
var MESSAGE_LINE = "\n[thread ";
|
|
20349
|
+
|
|
20350
|
+
// ../../hosted-hub/src/store/subagents.ts
|
|
20351
|
+
var UP = `up(session_id, parent) AS (
|
|
20352
|
+
SELECT session_id, parent FROM subagent_links WHERE session_id = ?
|
|
20353
|
+
UNION SELECT c.session_id, c.parent FROM subagent_links c JOIN up ON c.session_id = up.parent)`;
|
|
20354
|
+
var ROOT = "SELECT t.id, t.thread_id, t.bot FROM up JOIN turns t ON t.id = up.parent";
|
|
20355
|
+
var subagentStep = (sessionId2, step) => s(`WITH RECURSIVE ${UP} ${STEP_INSERT} SELECT ?, ?, ?, ?, ?, ?, ?, ?, ?, (SELECT id FROM (${ROOT})), NULL`, sessionId2, ...stepValues(sessionId2, step));
|
|
20356
|
+
var rootLog = (sessionId2, type, at) => s(`WITH RECURSIVE ${UP} INSERT INTO log (thread_id, type, data, at)
|
|
20357
|
+
SELECT thread_id, ?, json_object('threadId', thread_id, 'agent', bot, 'turnId', id), ? FROM (${ROOT})`, sessionId2, type, at);
|
|
20358
|
+
function linkStatements(sessionId2, parent, callId) {
|
|
20359
|
+
const subtree = `down(session_id) AS (SELECT ? UNION SELECT c.session_id FROM subagent_links c JOIN down ON c.parent = down.session_id)`;
|
|
20360
|
+
return [
|
|
20361
|
+
s("INSERT OR IGNORE INTO subagent_links (session_id, parent, call_id) VALUES (?, ?, ?)", sessionId2, parent, callId),
|
|
20362
|
+
s(`WITH RECURSIVE ${UP}, ${subtree} UPDATE usage_steps SET turn_id = (SELECT id FROM (${ROOT}))
|
|
20363
|
+
WHERE turn_id IS NULL AND session_id IN (SELECT session_id FROM down) AND EXISTS (${ROOT})`, sessionId2, sessionId2)
|
|
20364
|
+
];
|
|
20442
20365
|
}
|
|
20443
|
-
|
|
20444
|
-
|
|
20366
|
+
|
|
20367
|
+
// ../../hosted-hub/src/store/failures.ts
|
|
20368
|
+
var STATE2 = "json_extract(event, '$.state')";
|
|
20369
|
+
var failureId = (trigger, bot) => `evt_fail_${trigger}_${bot}`;
|
|
20370
|
+
var cardLog = (idSql, now, ...args) => s(`INSERT INTO log (thread_id, type, data, at) SELECT thread_id, 'message.updated', json_object('threadId', thread_id, 'messageId', id), ? FROM messages WHERE id = (${idSql}) AND changes() > 0`, now, ...args);
|
|
20371
|
+
function failureStatements(t, now) {
|
|
20372
|
+
if (!t.trigger) return [];
|
|
20373
|
+
const id = failureId(t.trigger, t.bot), detail = t.error ?? "unknown error";
|
|
20374
|
+
const turn = (status) => s("EXISTS (SELECT 1 FROM turns WHERE id = ? AND status = ?)", t.id, status);
|
|
20375
|
+
const failed = turn("failed"), settled = (status, next) => {
|
|
20376
|
+
const when = turn(status);
|
|
20377
|
+
return [s(`UPDATE messages SET event = json_set(event, '$.state', ?) WHERE id = ? AND ${STATE2} = 'retrying' AND ${when.sql}`, next, id, ...when.args), cardLog("?", now, id)];
|
|
20378
|
+
};
|
|
20379
|
+
return [
|
|
20380
|
+
...eventStatements(t.threadId, { kind: "turn_failed", agent: t.bot, triggerId: t.trigger, state: "failed", retries: 0, turnId: t.id, detail }, `${t.bot}'s turn failed: ${detail}`, now, id, failed),
|
|
20381
|
+
s(`UPDATE messages SET text = ?, event = json_set(event, '$.state', 'failed', '$.detail', ?, '$.turnId', ?, '$.retries', json_extract(event, '$.retries') + 1)
|
|
20382
|
+
WHERE id = ? AND ${STATE2} = 'retrying' AND json_extract(event, '$.turnId') <> ? AND ${failed.sql}`, `${t.bot}'s turn failed: ${detail}`, detail, t.id, id, t.id, ...failed.args),
|
|
20383
|
+
cardLog("?", now, id),
|
|
20384
|
+
...settled("done", "resolved"),
|
|
20385
|
+
...settled("cancelled", "failed")
|
|
20386
|
+
];
|
|
20445
20387
|
}
|
|
20388
|
+
var triggerOf = (t) => `(SELECT d.message_id FROM turn_deliveries td JOIN deliveries d ON d.id = td.delivery_id WHERE td.turn_id = ${t}.id ORDER BY d.rowid LIMIT 1)`;
|
|
20389
|
+
|
|
20390
|
+
// ../../lib/tags.mjs
|
|
20391
|
+
var TAG_FIELDS = ["project", "issueType"];
|
|
20392
|
+
var LABEL_FIELDS = [...TAG_FIELDS, "path"];
|
|
20393
|
+
|
|
20394
|
+
// ../../lib/subscriptions.mjs
|
|
20395
|
+
var STATUSES = Object.freeze(["idle", "working", "waiting"]);
|
|
20396
|
+
|
|
20397
|
+
// ../../hosted-hub/src/store/dash.ts
|
|
20398
|
+
var COLUMNS = { importance: "importance", importanceBy: "importance_by", lane: "lane", delay: "delay", reason: "reason", basis: "basis", deferred: "deferred", updatedAt: "classified_at", by: "classified_by" };
|
|
20399
|
+
var FIELDS = Object.keys(COLUMNS);
|
|
20400
|
+
var CLASSIFICATION_COLUMNS = FIELDS.map((f) => COLUMNS[f]).join(", ");
|
|
20401
|
+
var DASH_READS = [s("SELECT * FROM dash_stamps")];
|
|
20402
|
+
|
|
20403
|
+
// ../../hosted-hub/src/store/app.ts
|
|
20404
|
+
var APP_READS = [
|
|
20405
|
+
s("SELECT * FROM thread_meta"),
|
|
20406
|
+
s("SELECT * FROM read_states"),
|
|
20407
|
+
s("SELECT * FROM read_marks"),
|
|
20408
|
+
s("SELECT * FROM pins"),
|
|
20409
|
+
s("SELECT * FROM reactions WHERE active = 1"),
|
|
20410
|
+
s("SELECT * FROM human_profiles"),
|
|
20411
|
+
...DASH_READS
|
|
20412
|
+
];
|
|
20413
|
+
|
|
20414
|
+
// ../../hosted-hub/src/store/requests.ts
|
|
20415
|
+
var SCHEDULE_LIMIT = `r.kind = 'session-limit' AND r.thread_id LIKE 'sch-%' AND NOT ${onLocal("r.bot")} AND r.sweep_gave_up_at IS NULL`;
|
|
20416
|
+
var PENDING_REQUESTS_READ = s(`SELECT r.id, r.session_id, r.thread_id, r.bot, r.kind, r.prompt, r.options, r.allow_freeform, r.tool_name, r.submitted_at, r.created_at,
|
|
20417
|
+
(SELECT d.message_id FROM turn_deliveries td JOIN deliveries d ON d.id = td.delivery_id WHERE td.turn_id = r.turn_id ORDER BY d.rowid DESC LIMIT 1) AS message_id
|
|
20418
|
+
FROM input_requests r JOIN sessions s ON s.id = r.session_id
|
|
20419
|
+
WHERE r.status = 'pending' AND s.ended_at IS NULL AND NOT (${SCHEDULE_LIMIT}) ORDER BY r.created_at`);
|
|
20420
|
+
|
|
20421
|
+
// ../../hosted-hub/src/store/workspace-state.ts
|
|
20422
|
+
var LIVE_TURNS_READ = s(`SELECT thread_id,bot,started_at,reply_text,activity FROM turns WHERE status IN ${OPEN_TURN_STATUSES} ORDER BY started_at`);
|
|
20423
|
+
|
|
20424
|
+
// ../../hosted-hub/src/store/attachments.ts
|
|
20425
|
+
var MAX_ATTACHMENTS = 10;
|
|
20426
|
+
var OPEN_TURN = `(SELECT id FROM turns WHERE session_id = ? AND status IN ${OPEN_TURN_STATUSES} ORDER BY started_at DESC LIMIT 1)`;
|
|
20427
|
+
var ATTACHMENTS_READ = s("SELECT message_id, upload_id, name FROM message_attachments ORDER BY message_id, position");
|
|
20428
|
+
var unseenBy = (bot, thread, mid) => {
|
|
20429
|
+
const end = `COALESCE((SELECT rowid FROM messages WHERE id = ${mid}), 9223372036854775807)`;
|
|
20430
|
+
return `a.message_id = e.id AND e.thread_id = ${thread} AND e.author <> ${bot} AND e.rowid < ${end}
|
|
20431
|
+
AND e.rowid > COALESCE((SELECT MAX(p.rowid) FROM deliveries dd JOIN messages p ON p.id = dd.message_id WHERE p.thread_id = e.thread_id AND dd.bot = ${bot} AND p.rowid < ${end}), 0)`;
|
|
20432
|
+
};
|
|
20433
|
+
|
|
20434
|
+
// ../../hosted-hub/src/store/threads.ts
|
|
20435
|
+
var SUMMARY_READ = s("SELECT scope,text,by,at FROM summaries");
|
|
20436
|
+
|
|
20437
|
+
// ../../lib/work-phases.mjs
|
|
20438
|
+
var PHASES = Object.freeze(["planning", "working", "reviewing", "done"]);
|
|
20439
|
+
var MOVES = Object.freeze({
|
|
20440
|
+
"": ["planning", "working"],
|
|
20441
|
+
planning: ["working"],
|
|
20442
|
+
working: ["reviewing"],
|
|
20443
|
+
reviewing: ["working", "planning", "done"],
|
|
20444
|
+
done: []
|
|
20445
|
+
});
|
|
20446
|
+
var VERDICTS = Object.freeze(["ship", "fix-first", "rethink"]);
|
|
20446
20447
|
|
|
20447
20448
|
// ../../node_modules/.pnpm/zod@4.5.4/node_modules/zod/v4/core/util.js
|
|
20448
20449
|
var util_exports2 = {};
|