@electric-agent/studio 1.5.0 → 1.12.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/dist/api-schemas.d.ts +225 -0
- package/dist/api-schemas.d.ts.map +1 -0
- package/dist/api-schemas.js +95 -0
- package/dist/api-schemas.js.map +1 -0
- package/dist/bridge/claude-code-base.d.ts +121 -0
- package/dist/bridge/claude-code-base.d.ts.map +1 -0
- package/dist/bridge/claude-code-base.js +263 -0
- package/dist/bridge/claude-code-base.js.map +1 -0
- package/dist/bridge/claude-code-docker.d.ts +13 -73
- package/dist/bridge/claude-code-docker.d.ts.map +1 -1
- package/dist/bridge/claude-code-docker.js +91 -302
- package/dist/bridge/claude-code-docker.js.map +1 -1
- package/dist/bridge/claude-code-sprites.d.ts +12 -59
- package/dist/bridge/claude-code-sprites.d.ts.map +1 -1
- package/dist/bridge/claude-code-sprites.js +88 -281
- package/dist/bridge/claude-code-sprites.js.map +1 -1
- package/dist/bridge/claude-md-generator.d.ts +22 -5
- package/dist/bridge/claude-md-generator.d.ts.map +1 -1
- package/dist/bridge/claude-md-generator.js +81 -213
- package/dist/bridge/claude-md-generator.js.map +1 -1
- package/dist/bridge/codex-docker.d.ts +56 -51
- package/dist/bridge/codex-docker.js +222 -230
- package/dist/bridge/codex-json-parser.d.ts +11 -11
- package/dist/bridge/codex-json-parser.js +231 -238
- package/dist/bridge/codex-md-generator.d.ts +3 -3
- package/dist/bridge/codex-md-generator.js +42 -32
- package/dist/bridge/codex-sprites.d.ts +50 -45
- package/dist/bridge/codex-sprites.js +212 -222
- package/dist/bridge/daytona.d.ts +25 -25
- package/dist/bridge/daytona.js +131 -136
- package/dist/bridge/docker-stdio.d.ts +21 -21
- package/dist/bridge/docker-stdio.js +126 -132
- package/dist/bridge/hosted.d.ts +3 -2
- package/dist/bridge/hosted.d.ts.map +1 -1
- package/dist/bridge/hosted.js +4 -0
- package/dist/bridge/hosted.js.map +1 -1
- package/dist/bridge/message-parser.d.ts +24 -0
- package/dist/bridge/message-parser.d.ts.map +1 -0
- package/dist/bridge/message-parser.js +39 -0
- package/dist/bridge/message-parser.js.map +1 -0
- package/dist/bridge/role-skills.d.ts +25 -0
- package/dist/bridge/role-skills.d.ts.map +1 -0
- package/dist/bridge/role-skills.js +120 -0
- package/dist/bridge/role-skills.js.map +1 -0
- package/dist/bridge/room-messaging-skill.d.ts +11 -0
- package/dist/bridge/room-messaging-skill.d.ts.map +1 -0
- package/dist/bridge/room-messaging-skill.js +41 -0
- package/dist/bridge/room-messaging-skill.js.map +1 -0
- package/dist/bridge/sprites.d.ts +22 -22
- package/dist/bridge/sprites.js +123 -128
- package/dist/bridge/stream-json-parser.js +12 -5
- package/dist/bridge/stream-json-parser.js.map +1 -1
- package/dist/bridge/types.d.ts +4 -10
- package/dist/bridge/types.d.ts.map +1 -1
- package/dist/client/assets/index-BfvQSMwH.css +1 -0
- package/dist/client/assets/index-CiwD5LkP.js +235 -0
- package/dist/client/index.html +2 -2
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/invite-code.d.ts +5 -0
- package/dist/invite-code.d.ts.map +1 -0
- package/dist/invite-code.js +14 -0
- package/dist/invite-code.js.map +1 -0
- package/dist/project-utils.d.ts.map +1 -1
- package/dist/project-utils.js.map +1 -1
- package/dist/registry.d.ts +11 -4
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +1 -1
- package/dist/registry.js.map +1 -1
- package/dist/room-router.d.ts +73 -0
- package/dist/room-router.d.ts.map +1 -0
- package/dist/room-router.js +345 -0
- package/dist/room-router.js.map +1 -0
- package/dist/sandbox/docker.d.ts.map +1 -1
- package/dist/sandbox/docker.js +5 -6
- package/dist/sandbox/docker.js.map +1 -1
- package/dist/sandbox/index.d.ts +0 -1
- package/dist/sandbox/index.d.ts.map +1 -1
- package/dist/sandbox/index.js +0 -1
- package/dist/sandbox/index.js.map +1 -1
- package/dist/sandbox/sprites.d.ts.map +1 -1
- package/dist/sandbox/sprites.js +40 -10
- package/dist/sandbox/sprites.js.map +1 -1
- package/dist/sandbox/types.d.ts +4 -2
- package/dist/sandbox/types.d.ts.map +1 -1
- package/dist/server.d.ts +12 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +824 -309
- package/dist/server.js.map +1 -1
- package/dist/session-auth.d.ts +9 -0
- package/dist/session-auth.d.ts.map +1 -1
- package/dist/session-auth.js +30 -0
- package/dist/session-auth.js.map +1 -1
- package/dist/sessions.d.ts +7 -1
- package/dist/sessions.d.ts.map +1 -1
- package/dist/sessions.js.map +1 -1
- package/dist/streams.d.ts +2 -6
- package/dist/streams.d.ts.map +1 -1
- package/dist/streams.js +6 -17
- package/dist/streams.js.map +1 -1
- package/dist/validate.d.ts +10 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +24 -0
- package/dist/validate.js.map +1 -0
- package/package.json +6 -9
- package/dist/client/assets/index-DDzmxYub.js +0 -234
- package/dist/client/assets/index-DcP7prsZ.css +0 -1
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parses agent output (assistant_message text) for @room or @<name> messages.
|
|
3
|
+
*
|
|
4
|
+
* Convention:
|
|
5
|
+
* @room <body> → broadcast to all participants
|
|
6
|
+
* @<name> <body> → direct message to a specific participant
|
|
7
|
+
* @room DONE: <summary> → signal conversation completion
|
|
8
|
+
* @room GATE: <question> → request human input
|
|
9
|
+
*
|
|
10
|
+
* If no @room/@name prefix is found, returns null (agent chose silence).
|
|
11
|
+
* Uses the LAST match in the text so agents can do work first, then talk.
|
|
12
|
+
*/
|
|
13
|
+
export interface ParsedRoomMessage {
|
|
14
|
+
/** Recipient name, or undefined for broadcast */
|
|
15
|
+
to?: string;
|
|
16
|
+
/** Message body */
|
|
17
|
+
body: string;
|
|
18
|
+
/** True if body starts with "DONE:" — signals conversation end */
|
|
19
|
+
isDone: boolean;
|
|
20
|
+
/** True if body starts with "GATE:" — agent requests human input */
|
|
21
|
+
isGateRequest: boolean;
|
|
22
|
+
}
|
|
23
|
+
export declare function parseRoomMessage(text: string, _senderName: string, knownParticipants?: string[]): ParsedRoomMessage | null;
|
|
24
|
+
//# sourceMappingURL=message-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-parser.d.ts","sourceRoot":"","sources":["../../src/bridge/message-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,WAAW,iBAAiB;IACjC,iDAAiD;IACjD,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAA;IACZ,kEAAkE;IAClE,MAAM,EAAE,OAAO,CAAA;IACf,oEAAoE;IACpE,aAAa,EAAE,OAAO,CAAA;CACtB;AAKD,wBAAgB,gBAAgB,CAC/B,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EACnB,iBAAiB,CAAC,EAAE,MAAM,EAAE,GAC1B,iBAAiB,GAAG,IAAI,CA2B1B"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parses agent output (assistant_message text) for @room or @<name> messages.
|
|
3
|
+
*
|
|
4
|
+
* Convention:
|
|
5
|
+
* @room <body> → broadcast to all participants
|
|
6
|
+
* @<name> <body> → direct message to a specific participant
|
|
7
|
+
* @room DONE: <summary> → signal conversation completion
|
|
8
|
+
* @room GATE: <question> → request human input
|
|
9
|
+
*
|
|
10
|
+
* If no @room/@name prefix is found, returns null (agent chose silence).
|
|
11
|
+
* Uses the LAST match in the text so agents can do work first, then talk.
|
|
12
|
+
*/
|
|
13
|
+
// Matches @room or @<name> at the start of a line, capturing just the target.
|
|
14
|
+
const ROOM_PREFIX_RE = /^@(\S+)\s/gm;
|
|
15
|
+
export function parseRoomMessage(text, _senderName, knownParticipants) {
|
|
16
|
+
// Find all @room / @name positions in the text
|
|
17
|
+
const hits = [];
|
|
18
|
+
// Reset lastIndex — /g regexes retain state between exec() calls
|
|
19
|
+
ROOM_PREFIX_RE.lastIndex = 0;
|
|
20
|
+
let match = ROOM_PREFIX_RE.exec(text);
|
|
21
|
+
while (match !== null) {
|
|
22
|
+
const target = match[1];
|
|
23
|
+
if (target === "room" || knownParticipants?.includes(target)) {
|
|
24
|
+
hits.push({ target, startOfBody: match.index + match[0].length });
|
|
25
|
+
}
|
|
26
|
+
match = ROOM_PREFIX_RE.exec(text);
|
|
27
|
+
}
|
|
28
|
+
if (hits.length === 0)
|
|
29
|
+
return null;
|
|
30
|
+
// Use the last match — agent does work first, then talks
|
|
31
|
+
const last = hits[hits.length - 1];
|
|
32
|
+
// Body runs from after "@room " to end of string (or next hit, but we want the last one)
|
|
33
|
+
const body = text.slice(last.startOfBody).trim();
|
|
34
|
+
const to = last.target === "room" ? undefined : last.target;
|
|
35
|
+
const isDone = body.startsWith("DONE:");
|
|
36
|
+
const isGateRequest = body.startsWith("GATE:");
|
|
37
|
+
return { to, body, isDone, isGateRequest };
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=message-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-parser.js","sourceRoot":"","sources":["../../src/bridge/message-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAaH,8EAA8E;AAC9E,MAAM,cAAc,GAAG,aAAa,CAAA;AAEpC,MAAM,UAAU,gBAAgB,CAC/B,IAAY,EACZ,WAAmB,EACnB,iBAA4B;IAE5B,+CAA+C;IAC/C,MAAM,IAAI,GAAmD,EAAE,CAAA;IAE/D,iEAAiE;IACjE,cAAc,CAAC,SAAS,GAAG,CAAC,CAAA;IAC5B,IAAI,KAAK,GAA2B,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAC7D,OAAO,KAAK,KAAK,IAAI,EAAE,CAAC;QACvB,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;QACvB,IAAI,MAAM,KAAK,MAAM,IAAI,iBAAiB,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YAC9D,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAA;QAClE,CAAC;QACD,KAAK,GAAG,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAClC,CAAC;IAED,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IAElC,yDAAyD;IACzD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;IAElC,yFAAyF;IACzF,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,IAAI,EAAE,CAAA;IAChD,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAA;IAC3D,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAA;IACvC,MAAM,aAAa,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAA;IAE9C,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,CAAA;AAC3C,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Role-based skill loader.
|
|
3
|
+
*
|
|
4
|
+
* Maps agent roles to skill files and tool permission sets.
|
|
5
|
+
* Built-in roles (coder, reviewer) get specific behavioral guidelines
|
|
6
|
+
* and restricted tool permissions to enforce isolation between roles.
|
|
7
|
+
*/
|
|
8
|
+
export interface RoleSkill {
|
|
9
|
+
/** Canonical role name (e.g. "coder", "reviewer"). */
|
|
10
|
+
roleName: string;
|
|
11
|
+
/** Markdown content of the role skill file. */
|
|
12
|
+
skillContent: string;
|
|
13
|
+
/** Allowed tools for this role. When undefined, use default tools. */
|
|
14
|
+
allowedTools?: string[];
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Resolve a role string to its skill content and tool permissions.
|
|
18
|
+
* Returns undefined if the role doesn't match a built-in role.
|
|
19
|
+
*/
|
|
20
|
+
export declare function resolveRoleSkill(role?: string): RoleSkill | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* Get the list of all known built-in role names.
|
|
23
|
+
*/
|
|
24
|
+
export declare function getBuiltInRoles(): string[];
|
|
25
|
+
//# sourceMappingURL=role-skills.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"role-skills.d.ts","sourceRoot":"","sources":["../../src/bridge/role-skills.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAkGH,MAAM,WAAW,SAAS;IACzB,sDAAsD;IACtD,QAAQ,EAAE,MAAM,CAAA;IAChB,+CAA+C;IAC/C,YAAY,EAAE,MAAM,CAAA;IACpB,sEAAsE;IACtE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;CACvB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAsBrE;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,MAAM,EAAE,CAE1C"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Role-based skill loader.
|
|
3
|
+
*
|
|
4
|
+
* Maps agent roles to skill files and tool permission sets.
|
|
5
|
+
* Built-in roles (coder, reviewer) get specific behavioral guidelines
|
|
6
|
+
* and restricted tool permissions to enforce isolation between roles.
|
|
7
|
+
*/
|
|
8
|
+
import fs from "node:fs";
|
|
9
|
+
import path from "node:path";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
11
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// Role aliases → canonical role name
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
const ROLE_ALIASES = {
|
|
16
|
+
coder: "coder",
|
|
17
|
+
developer: "coder",
|
|
18
|
+
programmer: "coder",
|
|
19
|
+
engineer: "coder",
|
|
20
|
+
reviewer: "reviewer",
|
|
21
|
+
"code reviewer": "reviewer",
|
|
22
|
+
"pr reviewer": "reviewer",
|
|
23
|
+
};
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// Tool permissions per role
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
/** Full tool set — same as DEFAULT_ALLOWED_TOOLS in the bridges. */
|
|
28
|
+
const ALL_TOOLS = [
|
|
29
|
+
"Read",
|
|
30
|
+
"Write",
|
|
31
|
+
"Edit",
|
|
32
|
+
"Bash",
|
|
33
|
+
"Glob",
|
|
34
|
+
"Grep",
|
|
35
|
+
"WebSearch",
|
|
36
|
+
"TodoWrite",
|
|
37
|
+
"AskUserQuestion",
|
|
38
|
+
"Skill",
|
|
39
|
+
];
|
|
40
|
+
/**
|
|
41
|
+
* Coder gets full write access — can read, write, edit, run commands.
|
|
42
|
+
*/
|
|
43
|
+
const CODER_TOOLS = [...ALL_TOOLS];
|
|
44
|
+
/**
|
|
45
|
+
* Reviewer is read-only + Bash (for gh CLI).
|
|
46
|
+
* Cannot Write or Edit files — only review and comment.
|
|
47
|
+
*/
|
|
48
|
+
const REVIEWER_TOOLS = [
|
|
49
|
+
"Read",
|
|
50
|
+
"Bash",
|
|
51
|
+
"Glob",
|
|
52
|
+
"Grep",
|
|
53
|
+
"WebSearch",
|
|
54
|
+
"TodoWrite",
|
|
55
|
+
"AskUserQuestion",
|
|
56
|
+
"Skill",
|
|
57
|
+
];
|
|
58
|
+
const ROLE_TOOLS = {
|
|
59
|
+
coder: CODER_TOOLS,
|
|
60
|
+
reviewer: REVIEWER_TOOLS,
|
|
61
|
+
};
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
// Skill file loading
|
|
64
|
+
// ---------------------------------------------------------------------------
|
|
65
|
+
const roleSkillCache = new Map();
|
|
66
|
+
function loadSkillFile(roleName) {
|
|
67
|
+
const candidates = [
|
|
68
|
+
// From studio/dist/bridge/ → project root
|
|
69
|
+
path.resolve(__dirname, `../../../../.claude/skills/roles/${roleName}/SKILL.md`),
|
|
70
|
+
// From studio/src/bridge/ → project root (dev mode)
|
|
71
|
+
path.resolve(__dirname, `../../../.claude/skills/roles/${roleName}/SKILL.md`),
|
|
72
|
+
// Monorepo root from packages/studio/
|
|
73
|
+
path.resolve(__dirname, `../../.claude/skills/roles/${roleName}/SKILL.md`),
|
|
74
|
+
];
|
|
75
|
+
for (const candidate of candidates) {
|
|
76
|
+
try {
|
|
77
|
+
if (fs.existsSync(candidate)) {
|
|
78
|
+
return fs.readFileSync(candidate, "utf-8");
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
// Continue to next candidate
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Resolve a role string to its skill content and tool permissions.
|
|
89
|
+
* Returns undefined if the role doesn't match a built-in role.
|
|
90
|
+
*/
|
|
91
|
+
export function resolveRoleSkill(role) {
|
|
92
|
+
if (!role)
|
|
93
|
+
return undefined;
|
|
94
|
+
const normalized = role.toLowerCase().trim();
|
|
95
|
+
const roleName = ROLE_ALIASES[normalized];
|
|
96
|
+
if (!roleName)
|
|
97
|
+
return undefined;
|
|
98
|
+
// Load skill content (with cache)
|
|
99
|
+
if (!roleSkillCache.has(roleName)) {
|
|
100
|
+
const content = loadSkillFile(roleName);
|
|
101
|
+
if (!content) {
|
|
102
|
+
console.warn(`[role-skills] Skill file not found for role "${roleName}"`);
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
roleSkillCache.set(roleName, content);
|
|
106
|
+
}
|
|
107
|
+
const skillContent = roleSkillCache.get(roleName);
|
|
108
|
+
return {
|
|
109
|
+
roleName,
|
|
110
|
+
skillContent,
|
|
111
|
+
allowedTools: ROLE_TOOLS[roleName],
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Get the list of all known built-in role names.
|
|
116
|
+
*/
|
|
117
|
+
export function getBuiltInRoles() {
|
|
118
|
+
return [...new Set(Object.values(ROLE_ALIASES))];
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=role-skills.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"role-skills.js","sourceRoot":"","sources":["../../src/bridge/role-skills.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The room-messaging skill content as a string constant.
|
|
3
|
+
*
|
|
4
|
+
* Exported so the server can write the skill into sandboxes, ensuring agents
|
|
5
|
+
* have persistent access to the multi-agent messaging protocol even after the
|
|
6
|
+
* initial discovery prompt scrolls out of context.
|
|
7
|
+
*
|
|
8
|
+
* The content must stay in sync with .claude/skills/room-messaging/SKILL.md.
|
|
9
|
+
*/
|
|
10
|
+
export declare const roomMessagingSkillContent: string;
|
|
11
|
+
//# sourceMappingURL=room-messaging-skill.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"room-messaging-skill.d.ts","sourceRoot":"","sources":["../../src/bridge/room-messaging-skill.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAoCH,eAAO,MAAM,yBAAyB,QAAqB,CAAA"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The room-messaging skill content as a string constant.
|
|
3
|
+
*
|
|
4
|
+
* Exported so the server can write the skill into sandboxes, ensuring agents
|
|
5
|
+
* have persistent access to the multi-agent messaging protocol even after the
|
|
6
|
+
* initial discovery prompt scrolls out of context.
|
|
7
|
+
*
|
|
8
|
+
* The content must stay in sync with .claude/skills/room-messaging/SKILL.md.
|
|
9
|
+
*/
|
|
10
|
+
import fs from "node:fs";
|
|
11
|
+
import path from "node:path";
|
|
12
|
+
import { fileURLToPath } from "node:url";
|
|
13
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
14
|
+
/**
|
|
15
|
+
* Read the room-messaging skill from the project root.
|
|
16
|
+
* Falls back to an empty string if the file can't be found.
|
|
17
|
+
*/
|
|
18
|
+
function loadSkillContent() {
|
|
19
|
+
const candidates = [
|
|
20
|
+
// From studio/dist/bridge/ → project root .claude/skills/room-messaging/SKILL.md
|
|
21
|
+
path.resolve(__dirname, "../../../../.claude/skills/room-messaging/SKILL.md"),
|
|
22
|
+
// From studio/src/bridge/ → project root (dev mode)
|
|
23
|
+
path.resolve(__dirname, "../../../.claude/skills/room-messaging/SKILL.md"),
|
|
24
|
+
// Monorepo root from packages/studio/
|
|
25
|
+
path.resolve(__dirname, "../../.claude/skills/room-messaging/SKILL.md"),
|
|
26
|
+
];
|
|
27
|
+
for (const candidate of candidates) {
|
|
28
|
+
try {
|
|
29
|
+
if (fs.existsSync(candidate)) {
|
|
30
|
+
return fs.readFileSync(candidate, "utf-8");
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// Continue to next candidate
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
console.warn("[room-messaging-skill] Could not find SKILL.md in any expected location");
|
|
38
|
+
return "";
|
|
39
|
+
}
|
|
40
|
+
export const roomMessagingSkillContent = loadSkillContent();
|
|
41
|
+
//# sourceMappingURL=room-messaging-skill.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"room-messaging-skill.js","sourceRoot":"","sources":["../../src/bridge/room-messaging-skill.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAExC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;AAE9D;;;GAGG;AACH,SAAS,gBAAgB;IACxB,MAAM,UAAU,GAAG;QAClB,iFAAiF;QACjF,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,oDAAoD,CAAC;QAC7E,oDAAoD;QACpD,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,iDAAiD,CAAC;QAC1E,sCAAsC;QACtC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,8CAA8C,CAAC;KACvE,CAAA;IAED,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACpC,IAAI,CAAC;YACJ,IAAI,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC9B,OAAO,EAAE,CAAC,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;YAC3C,CAAC;QACF,CAAC;QAAC,MAAM,CAAC;YACR,6BAA6B;QAC9B,CAAC;IACF,CAAC;IAED,OAAO,CAAC,IAAI,CAAC,yEAAyE,CAAC,CAAA;IACvF,OAAO,EAAE,CAAA;AACV,CAAC;AAED,MAAM,CAAC,MAAM,yBAAyB,GAAG,gBAAgB,EAAE,CAAA"}
|
package/dist/bridge/sprites.d.ts
CHANGED
|
@@ -6,27 +6,27 @@
|
|
|
6
6
|
* the Sprites session (for agent communication). The agent inside the
|
|
7
7
|
* sprite uses the stdio adapter — the bridge relays events to the stream.
|
|
8
8
|
*/
|
|
9
|
-
import type { EngineEvent } from "@electric-agent/protocol"
|
|
10
|
-
import type { Sprite } from "@fly/sprites"
|
|
11
|
-
import type { StreamConnectionInfo } from "../streams.js"
|
|
12
|
-
import type { SessionBridge } from "./types.js"
|
|
9
|
+
import type { EngineEvent } from "@electric-agent/protocol"
|
|
10
|
+
import type { Sprite } from "@fly/sprites"
|
|
11
|
+
import type { StreamConnectionInfo } from "../streams.js"
|
|
12
|
+
import type { SessionBridge } from "./types.js"
|
|
13
13
|
export declare class SpritesStdioBridge implements SessionBridge {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
14
|
+
readonly sessionId: string
|
|
15
|
+
readonly streamUrl: string
|
|
16
|
+
readonly streamHeaders: Record<string, string>
|
|
17
|
+
private sprite
|
|
18
|
+
private writer
|
|
19
|
+
private agentEventCallbacks
|
|
20
|
+
private completeCallbacks
|
|
21
|
+
private closed
|
|
22
|
+
private cmd
|
|
23
|
+
constructor(sessionId: string, connection: StreamConnectionInfo, sprite: Sprite)
|
|
24
|
+
emit(event: EngineEvent): Promise<void>
|
|
25
|
+
sendCommand(cmd: Record<string, unknown>): Promise<void>
|
|
26
|
+
sendGateResponse(gate: string, value: Record<string, unknown>): Promise<void>
|
|
27
|
+
onAgentEvent(cb: (event: EngineEvent) => void): void
|
|
28
|
+
onComplete(cb: (success: boolean) => void): void
|
|
29
|
+
start(): Promise<void>
|
|
30
|
+
close(): void
|
|
31
31
|
}
|
|
32
|
-
//# sourceMappingURL=sprites.d.ts.map
|
|
32
|
+
//# sourceMappingURL=sprites.d.ts.map
|
package/dist/bridge/sprites.js
CHANGED
|
@@ -6,133 +6,128 @@
|
|
|
6
6
|
* the Sprites session (for agent communication). The agent inside the
|
|
7
7
|
* sprite uses the stdio adapter — the bridge relays events to the stream.
|
|
8
8
|
*/
|
|
9
|
-
import * as readline from "node:readline"
|
|
10
|
-
import { DurableStream } from "@durable-streams/client"
|
|
11
|
-
|
|
9
|
+
import * as readline from "node:readline"
|
|
10
|
+
import { DurableStream } from "@durable-streams/client"
|
|
11
|
+
|
|
12
|
+
const SPRITES_SESSION_ID = "agent-session"
|
|
12
13
|
export class SpritesStdioBridge {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
catch {
|
|
132
|
-
// Process may already be dead
|
|
133
|
-
}
|
|
134
|
-
this.cmd = null;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
14
|
+
sessionId
|
|
15
|
+
streamUrl
|
|
16
|
+
streamHeaders
|
|
17
|
+
sprite
|
|
18
|
+
writer
|
|
19
|
+
agentEventCallbacks = []
|
|
20
|
+
completeCallbacks = []
|
|
21
|
+
closed = false
|
|
22
|
+
cmd = null
|
|
23
|
+
constructor(sessionId, connection, sprite) {
|
|
24
|
+
this.sessionId = sessionId
|
|
25
|
+
this.streamUrl = connection.url
|
|
26
|
+
this.streamHeaders = connection.headers
|
|
27
|
+
this.sprite = sprite
|
|
28
|
+
this.writer = new DurableStream({
|
|
29
|
+
url: connection.url,
|
|
30
|
+
headers: connection.headers,
|
|
31
|
+
contentType: "application/json",
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
async emit(event) {
|
|
35
|
+
if (this.closed) return
|
|
36
|
+
const msg = { source: "server", ...event }
|
|
37
|
+
await this.writer.append(JSON.stringify(msg))
|
|
38
|
+
}
|
|
39
|
+
async sendCommand(cmd) {
|
|
40
|
+
if (this.closed || !this.cmd) return
|
|
41
|
+
const line = JSON.stringify({ type: "command", ...cmd })
|
|
42
|
+
this.cmd.stdin.write(`${line}\n`)
|
|
43
|
+
}
|
|
44
|
+
async sendGateResponse(gate, value) {
|
|
45
|
+
if (this.closed || !this.cmd) return
|
|
46
|
+
const line = JSON.stringify({ type: "gate_response", gate, ...value })
|
|
47
|
+
this.cmd.stdin.write(`${line}\n`)
|
|
48
|
+
}
|
|
49
|
+
onAgentEvent(cb) {
|
|
50
|
+
this.agentEventCallbacks.push(cb)
|
|
51
|
+
}
|
|
52
|
+
onComplete(cb) {
|
|
53
|
+
this.completeCallbacks.push(cb)
|
|
54
|
+
}
|
|
55
|
+
async start() {
|
|
56
|
+
if (this.closed) return
|
|
57
|
+
// Create a persistent session in the sprite so we can reconnect if needed
|
|
58
|
+
this.cmd = this.sprite.createSession(
|
|
59
|
+
"bash",
|
|
60
|
+
[
|
|
61
|
+
"-c",
|
|
62
|
+
"source /etc/profile.d/npm-global.sh 2>/dev/null; source /etc/profile.d/electric-agent.sh && electric-agent headless",
|
|
63
|
+
],
|
|
64
|
+
{ detachable: true, sessionId: SPRITES_SESSION_ID },
|
|
65
|
+
)
|
|
66
|
+
console.log(`[sprites-bridge] Agent started: session=${this.sessionId}`)
|
|
67
|
+
// Read stdout line by line (NDJSON)
|
|
68
|
+
const rl = readline.createInterface({
|
|
69
|
+
input: this.cmd.stdout,
|
|
70
|
+
terminal: false,
|
|
71
|
+
})
|
|
72
|
+
rl.on("line", (line) => {
|
|
73
|
+
if (this.closed) return
|
|
74
|
+
const trimmed = line.trim()
|
|
75
|
+
if (!trimmed) return
|
|
76
|
+
let event
|
|
77
|
+
try {
|
|
78
|
+
event = JSON.parse(trimmed)
|
|
79
|
+
} catch {
|
|
80
|
+
console.log(`[sprites-bridge] Non-JSON stdout: ${trimmed}`)
|
|
81
|
+
return
|
|
82
|
+
}
|
|
83
|
+
// Write to Durable Stream for UI
|
|
84
|
+
const msg = { source: "agent", ...event }
|
|
85
|
+
this.writer.append(JSON.stringify(msg)).catch(() => {})
|
|
86
|
+
// Dispatch to callbacks
|
|
87
|
+
for (const cb of this.agentEventCallbacks) {
|
|
88
|
+
try {
|
|
89
|
+
cb(event)
|
|
90
|
+
} catch {
|
|
91
|
+
// Swallow callback errors
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// Detect session_end
|
|
95
|
+
if (event.type === "session_end" && "success" in event) {
|
|
96
|
+
const success = event.success
|
|
97
|
+
for (const cb of this.completeCallbacks) {
|
|
98
|
+
try {
|
|
99
|
+
cb(success)
|
|
100
|
+
} catch {
|
|
101
|
+
// Swallow callback errors
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
})
|
|
106
|
+
// Log stderr
|
|
107
|
+
const stderrRl = readline.createInterface({
|
|
108
|
+
input: this.cmd.stderr,
|
|
109
|
+
terminal: false,
|
|
110
|
+
})
|
|
111
|
+
stderrRl.on("line", (line) => {
|
|
112
|
+
if (!this.closed) {
|
|
113
|
+
console.error(`[sprites-bridge:stderr] ${line}`)
|
|
114
|
+
}
|
|
115
|
+
})
|
|
116
|
+
// Handle process exit
|
|
117
|
+
this.cmd.on("exit", (code) => {
|
|
118
|
+
console.log(`[sprites-bridge] Agent process exited: code=${code} session=${this.sessionId}`)
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
close() {
|
|
122
|
+
this.closed = true
|
|
123
|
+
if (this.cmd) {
|
|
124
|
+
try {
|
|
125
|
+
this.cmd.kill()
|
|
126
|
+
} catch {
|
|
127
|
+
// Process may already be dead
|
|
128
|
+
}
|
|
129
|
+
this.cmd = null
|
|
130
|
+
}
|
|
131
|
+
}
|
|
137
132
|
}
|
|
138
|
-
//# sourceMappingURL=sprites.js.map
|
|
133
|
+
//# sourceMappingURL=sprites.js.map
|
|
@@ -157,15 +157,22 @@ function handleUser(msg, state) {
|
|
|
157
157
|
}
|
|
158
158
|
return events;
|
|
159
159
|
}
|
|
160
|
-
function handleResult(msg,
|
|
161
|
-
const events = [];
|
|
160
|
+
function handleResult(msg, _state) {
|
|
162
161
|
const success = msg.subtype === "success";
|
|
163
|
-
|
|
162
|
+
const event = {
|
|
164
163
|
type: "session_end",
|
|
165
164
|
success,
|
|
166
165
|
ts: ts(),
|
|
167
|
-
}
|
|
168
|
-
|
|
166
|
+
};
|
|
167
|
+
if (msg.cost_usd != null)
|
|
168
|
+
event.cost_usd = msg.cost_usd;
|
|
169
|
+
if (msg.num_turns != null)
|
|
170
|
+
event.num_turns = msg.num_turns;
|
|
171
|
+
if (msg.duration_ms != null)
|
|
172
|
+
event.duration_ms = msg.duration_ms;
|
|
173
|
+
if (msg.duration_api_ms != null)
|
|
174
|
+
event.duration_api_ms = msg.duration_api_ms;
|
|
175
|
+
return [event];
|
|
169
176
|
}
|
|
170
177
|
// ---------------------------------------------------------------------------
|
|
171
178
|
// Helpers
|