@geteigen/cli 0.0.0-stage → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +154 -2
- package/dist/agent.d.ts +32 -0
- package/dist/agent.js +246 -0
- package/dist/auth.d.ts +5 -0
- package/dist/auth.js +33 -0
- package/dist/cli/src/agent.d.ts +24 -0
- package/dist/cli/src/agent.js +263 -0
- package/dist/cli/src/agentTools.d.ts +6 -0
- package/dist/cli/src/agentTools.js +104 -0
- package/dist/cli/src/auth.d.ts +5 -0
- package/dist/cli/src/auth.js +33 -0
- package/dist/cli/src/cliEnv.d.ts +6 -0
- package/dist/cli/src/cliEnv.js +26 -0
- package/dist/cli/src/config.d.ts +11 -0
- package/dist/cli/src/config.js +51 -0
- package/dist/cli/src/cursorAuth.d.ts +10 -0
- package/dist/cli/src/cursorAuth.js +92 -0
- package/dist/cli/src/gitAuthEnv.d.ts +5 -0
- package/dist/cli/src/gitAuthEnv.js +35 -0
- package/dist/cli/src/index.d.ts +2 -0
- package/dist/cli/src/index.js +244 -0
- package/dist/cli/src/listModels.d.ts +14 -0
- package/dist/cli/src/listModels.js +196 -0
- package/dist/cli/src/listSessions.d.ts +9 -0
- package/dist/cli/src/listSessions.js +37 -0
- package/dist/cli/src/machineConfig.d.ts +20 -0
- package/dist/cli/src/machineConfig.js +66 -0
- package/dist/cli/src/pickFolder.d.ts +5 -0
- package/dist/cli/src/pickFolder.js +65 -0
- package/dist/cli/src/proc.d.ts +31 -0
- package/dist/cli/src/proc.js +97 -0
- package/dist/cli/src/service.d.ts +17 -0
- package/dist/cli/src/service.js +255 -0
- package/dist/cli/src/workRoots.d.ts +30 -0
- package/dist/cli/src/workRoots.js +228 -0
- package/dist/cli/src/worker.d.ts +8 -0
- package/dist/cli/src/worker.js +51 -0
- package/dist/cli/src/workerCore.d.ts +47 -0
- package/dist/cli/src/workerCore.js +721 -0
- package/dist/cli/src/workspace.d.ts +10 -0
- package/dist/cli/src/workspace.js +329 -0
- package/dist/config.d.ts +11 -0
- package/dist/config.js +51 -0
- package/dist/cursorAuth.d.ts +10 -0
- package/dist/cursorAuth.js +95 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +108 -0
- package/dist/listModels.d.ts +6 -0
- package/dist/listModels.js +62 -0
- package/dist/listSessions.d.ts +9 -0
- package/dist/listSessions.js +37 -0
- package/dist/pickFolder.d.ts +5 -0
- package/dist/pickFolder.js +65 -0
- package/dist/shared/actionDrafts.d.ts +83 -0
- package/dist/shared/actionDrafts.js +169 -0
- package/dist/shared/agentArtifacts.d.ts +49 -0
- package/dist/shared/agentArtifacts.js +169 -0
- package/dist/shared/agentLabel.d.ts +18 -0
- package/dist/shared/agentLabel.js +22 -0
- package/dist/shared/agentMentions.d.ts +38 -0
- package/dist/shared/agentMentions.js +110 -0
- package/dist/shared/agentRepos.d.ts +28 -0
- package/dist/shared/agentRepos.js +60 -0
- package/dist/shared/agentRunStatus.d.ts +12 -0
- package/dist/shared/agentRunStatus.js +17 -0
- package/dist/shared/agentUsage.d.ts +24 -0
- package/dist/shared/agentUsage.js +139 -0
- package/dist/shared/approvals.d.ts +44 -0
- package/dist/shared/approvals.js +97 -0
- package/dist/shared/askEigen.d.ts +190 -0
- package/dist/shared/askEigen.js +76 -0
- package/dist/shared/attribution.d.ts +25 -0
- package/dist/shared/attribution.js +63 -0
- package/dist/shared/automations.d.ts +135 -0
- package/dist/shared/automations.js +553 -0
- package/dist/shared/backends/claudeCode.d.ts +44 -0
- package/dist/shared/backends/claudeCode.js +548 -0
- package/dist/shared/backends/codex.d.ts +15 -0
- package/dist/shared/backends/codex.js +287 -0
- package/dist/shared/backends/cursor.d.ts +111 -0
- package/dist/shared/backends/cursor.js +1216 -0
- package/dist/shared/backends/index.d.ts +18 -0
- package/dist/shared/backends/index.js +58 -0
- package/dist/shared/backends/opencode.d.ts +43 -0
- package/dist/shared/backends/opencode.js +415 -0
- package/dist/shared/backends/types.d.ts +171 -0
- package/dist/shared/backends/types.js +19 -0
- package/dist/shared/backends/usage.d.ts +8 -0
- package/dist/shared/backends/usage.js +71 -0
- package/dist/shared/billingPlans.d.ts +60 -0
- package/dist/shared/billingPlans.js +262 -0
- package/dist/shared/brain.d.ts +353 -0
- package/dist/shared/brain.js +487 -0
- package/dist/shared/clarifyingQuestions.d.ts +13 -0
- package/dist/shared/clarifyingQuestions.js +207 -0
- package/dist/shared/claudeModels.d.ts +10 -0
- package/dist/shared/claudeModels.js +52 -0
- package/dist/shared/cloudBackends.d.ts +21 -0
- package/dist/shared/cloudBackends.js +29 -0
- package/dist/shared/codexModels.d.ts +10 -0
- package/dist/shared/codexModels.js +28 -0
- package/dist/shared/contextWindows.d.ts +2 -0
- package/dist/shared/contextWindows.js +30 -0
- package/dist/shared/cursorMcpConfig.d.ts +14 -0
- package/dist/shared/cursorMcpConfig.js +43 -0
- package/dist/shared/cursorProjectMcp.d.ts +10 -0
- package/dist/shared/cursorProjectMcp.js +42 -0
- package/dist/shared/docs.d.ts +59 -0
- package/dist/shared/docs.js +87 -0
- package/dist/shared/events.d.ts +932 -0
- package/dist/shared/events.js +57 -0
- package/dist/shared/integrations.d.ts +266 -0
- package/dist/shared/integrations.js +449 -0
- package/dist/shared/issueTranscript.d.ts +64 -0
- package/dist/shared/issueTranscript.js +387 -0
- package/dist/shared/issues.d.ts +139 -0
- package/dist/shared/issues.js +217 -0
- package/dist/shared/machines.d.ts +119 -0
- package/dist/shared/machines.js +58 -0
- package/dist/shared/markdownMedia.d.ts +13 -0
- package/dist/shared/markdownMedia.js +41 -0
- package/dist/shared/notificationText.d.ts +6 -0
- package/dist/shared/notificationText.js +38 -0
- package/dist/shared/opencodeModels.d.ts +19 -0
- package/dist/shared/opencodeModels.js +62 -0
- package/dist/shared/opencodeSession.d.ts +7 -0
- package/dist/shared/opencodeSession.js +30 -0
- package/dist/shared/orchestration.d.ts +58 -0
- package/dist/shared/orchestration.js +12 -0
- package/dist/shared/orgs.d.ts +47 -0
- package/dist/shared/orgs.js +49 -0
- package/dist/shared/personalOrg.d.ts +5 -0
- package/dist/shared/personalOrg.js +12 -0
- package/dist/shared/plans.d.ts +3 -0
- package/dist/shared/plans.js +19 -0
- package/dist/shared/providerModels.d.ts +40 -0
- package/dist/shared/providerModels.js +503 -0
- package/dist/shared/roomAttention.d.ts +17 -0
- package/dist/shared/roomAttention.js +36 -0
- package/dist/shared/roomContext.d.ts +120 -0
- package/dist/shared/roomContext.js +133 -0
- package/dist/shared/roomPermissions.d.ts +56 -0
- package/dist/shared/roomPermissions.js +133 -0
- package/dist/shared/sessionSeats.d.ts +29 -0
- package/dist/shared/sessionSeats.js +57 -0
- package/dist/shared/slashCommands.d.ts +39 -0
- package/dist/shared/slashCommands.js +166 -0
- package/dist/shared/space.d.ts +33 -0
- package/dist/shared/space.js +54 -0
- package/dist/shared/spaceFeed.d.ts +78 -0
- package/dist/shared/spaceFeed.js +194 -0
- package/dist/shared/stringifyUnknown.d.ts +1 -0
- package/dist/shared/stringifyUnknown.js +1 -0
- package/dist/shared/swarm.d.ts +320 -0
- package/dist/shared/swarm.js +328 -0
- package/dist/shared/swarmVisual.d.ts +79 -0
- package/dist/shared/swarmVisual.js +212 -0
- package/dist/shared/terminalFilter.d.ts +13 -0
- package/dist/shared/terminalFilter.js +46 -0
- package/dist/shared/toolIntegration.d.ts +56 -0
- package/dist/shared/toolIntegration.js +180 -0
- package/dist/shared/typing.d.ts +9 -0
- package/dist/shared/typing.js +37 -0
- package/dist/shared/uploads.d.ts +31 -0
- package/dist/shared/uploads.js +65 -0
- package/dist/shared/voiceApprovals.d.ts +36 -0
- package/dist/shared/voiceApprovals.js +229 -0
- package/dist/shared/workspace.d.ts +49 -0
- package/dist/shared/workspace.js +53 -0
- package/dist/worker.d.ts +1 -0
- package/dist/worker.js +266 -0
- package/package.json +51 -4
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/** Room-scoped repo map + shared memory contracts. */
|
|
2
|
+
export const MEMORY_KINDS = [
|
|
3
|
+
"goal",
|
|
4
|
+
"decision",
|
|
5
|
+
"constraint",
|
|
6
|
+
"discovery",
|
|
7
|
+
"handoff",
|
|
8
|
+
"feedback",
|
|
9
|
+
];
|
|
10
|
+
export const MEMORY_STATUSES = [
|
|
11
|
+
"active",
|
|
12
|
+
"proposed",
|
|
13
|
+
"archived",
|
|
14
|
+
"superseded",
|
|
15
|
+
];
|
|
16
|
+
export const MEMORY_SOURCES = ["human", "auto", "agent_proposed"];
|
|
17
|
+
export const AUTO_MEMORY_MODES = ["off", "extract"];
|
|
18
|
+
export function isMemorySource(v) {
|
|
19
|
+
return typeof v === "string" && MEMORY_SOURCES.includes(v);
|
|
20
|
+
}
|
|
21
|
+
export function parseAutoMemoryMode(raw, fallback = "extract") {
|
|
22
|
+
if (typeof raw !== "string")
|
|
23
|
+
return fallback;
|
|
24
|
+
const n = raw.trim().toLowerCase();
|
|
25
|
+
if (n === "off" || n === "extract")
|
|
26
|
+
return n;
|
|
27
|
+
return fallback;
|
|
28
|
+
}
|
|
29
|
+
export const MEMORY_TITLE_MAX = 160;
|
|
30
|
+
export const MEMORY_CONTENT_MAX = 4000;
|
|
31
|
+
export const CONTEXT_BUDGET_CHARS = 12_000;
|
|
32
|
+
export const BASELINE_BUDGET_CHARS = 8_000;
|
|
33
|
+
export const TASK_PACK_FILES = 12;
|
|
34
|
+
export const TASK_PACK_EDGES = 20;
|
|
35
|
+
const SECRET_PATTERNS = [
|
|
36
|
+
/\b(sk|rk|pk)-[A-Za-z0-9_-]{16,}\b/g,
|
|
37
|
+
/\b(ghp|gho|github_pat)_[A-Za-z0-9_]{20,}\b/g,
|
|
38
|
+
/\b(xox[baprs]-)[A-Za-z0-9-]{10,}\b/g,
|
|
39
|
+
/\bAKIA[0-9A-Z]{16}\b/g,
|
|
40
|
+
/\bBearer\s+[A-Za-z0-9._\-+=/]{20,}\b/gi,
|
|
41
|
+
];
|
|
42
|
+
export function isMemoryKind(v) {
|
|
43
|
+
return typeof v === "string" && MEMORY_KINDS.includes(v);
|
|
44
|
+
}
|
|
45
|
+
/** Standing instructions / jailbreak-shaped text must never auto-accept. */
|
|
46
|
+
export function looksLikeMemoryInjection(input) {
|
|
47
|
+
const q = input.toLowerCase();
|
|
48
|
+
return (/\b(ignore|disregard)\s+(all\s+)?(previous|prior|system)\b/.test(q) ||
|
|
49
|
+
/\b(you are now|jailbreak|system prompt)\b/.test(q) ||
|
|
50
|
+
/\b(always obey|must follow these instructions|from now on you)\b/.test(q) ||
|
|
51
|
+
/\b(do not follow|override)\s+(the\s+)?(safety|platform|user request)\b/.test(q));
|
|
52
|
+
}
|
|
53
|
+
export function isMemoryStatus(v) {
|
|
54
|
+
return typeof v === "string" && MEMORY_STATUSES.includes(v);
|
|
55
|
+
}
|
|
56
|
+
export function stripControlChars(input) {
|
|
57
|
+
return input.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g, "");
|
|
58
|
+
}
|
|
59
|
+
export function redactSecrets(input) {
|
|
60
|
+
let out = input;
|
|
61
|
+
for (const re of SECRET_PATTERNS) {
|
|
62
|
+
out = out.replace(re, "[redacted]");
|
|
63
|
+
}
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
export function sanitizeMemoryText(input, max) {
|
|
67
|
+
if (typeof input !== "string")
|
|
68
|
+
return "";
|
|
69
|
+
return redactSecrets(stripControlChars(input)).trim().slice(0, max);
|
|
70
|
+
}
|
|
71
|
+
export function tokenizeQuery(text) {
|
|
72
|
+
const stop = new Set([
|
|
73
|
+
"a",
|
|
74
|
+
"an",
|
|
75
|
+
"the",
|
|
76
|
+
"to",
|
|
77
|
+
"for",
|
|
78
|
+
"with",
|
|
79
|
+
"from",
|
|
80
|
+
"and",
|
|
81
|
+
"or",
|
|
82
|
+
"of",
|
|
83
|
+
"on",
|
|
84
|
+
"in",
|
|
85
|
+
"by",
|
|
86
|
+
"you",
|
|
87
|
+
"your",
|
|
88
|
+
"can",
|
|
89
|
+
"please",
|
|
90
|
+
"fix",
|
|
91
|
+
"update",
|
|
92
|
+
"make",
|
|
93
|
+
"show",
|
|
94
|
+
"change",
|
|
95
|
+
"do",
|
|
96
|
+
"it",
|
|
97
|
+
"this",
|
|
98
|
+
"that",
|
|
99
|
+
"api",
|
|
100
|
+
]);
|
|
101
|
+
const words = text.toLowerCase().match(/[a-zA-Z0-9_]+/g) || [];
|
|
102
|
+
const out = [];
|
|
103
|
+
const seen = new Set();
|
|
104
|
+
for (const w of words) {
|
|
105
|
+
if (w.length < 2 || stop.has(w) || seen.has(w))
|
|
106
|
+
continue;
|
|
107
|
+
seen.add(w);
|
|
108
|
+
out.push(w);
|
|
109
|
+
}
|
|
110
|
+
return out;
|
|
111
|
+
}
|
|
112
|
+
export function classifyIntent(query) {
|
|
113
|
+
const q = query.toLowerCase();
|
|
114
|
+
if (/\b(GET|POST|PUT|DELETE|PATCH)\s+\//i.test(query))
|
|
115
|
+
return "debug";
|
|
116
|
+
if (/\b(why|explain|architecture|how does)\b/.test(q))
|
|
117
|
+
return "explain";
|
|
118
|
+
if (/\b(error|bug|crash|failing|broken|exception|404|500)\b/.test(q)) {
|
|
119
|
+
return "debug";
|
|
120
|
+
}
|
|
121
|
+
if (/\b(test|coverage|spec)\b/.test(q))
|
|
122
|
+
return "test";
|
|
123
|
+
if (/\b(refactor|cleanup|simplify|optimize)\b/.test(q))
|
|
124
|
+
return "refactor";
|
|
125
|
+
if (/\b(add|create|implement|build|support)\b/.test(q))
|
|
126
|
+
return "feature";
|
|
127
|
+
if (/\b(fix|update|change|edit|modify|patch)\b/.test(q))
|
|
128
|
+
return "edit";
|
|
129
|
+
return "general";
|
|
130
|
+
}
|
|
131
|
+
export function wrapUntrustedBlock(tag, attrs, body) {
|
|
132
|
+
return `<${tag}${attrs ? ` ${attrs}` : ""}>\n${body.trim()}\n</${tag}>`;
|
|
133
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Room collaboration permissions.
|
|
3
|
+
*
|
|
4
|
+
* Roles:
|
|
5
|
+
* - owner: room host
|
|
6
|
+
* - editor: can collaborate (subject to control mode)
|
|
7
|
+
* - viewer: read-only
|
|
8
|
+
*
|
|
9
|
+
* Control modes:
|
|
10
|
+
* - open: any editor can steer
|
|
11
|
+
* - driver: only the current driver (or host) can steer
|
|
12
|
+
* - host: only the host can steer
|
|
13
|
+
*/
|
|
14
|
+
export type RoomRole = "owner" | "editor" | "viewer";
|
|
15
|
+
export type RoomInviteRole = "editor" | "viewer";
|
|
16
|
+
export type ControlMode = "open" | "driver" | "host";
|
|
17
|
+
/**
|
|
18
|
+
* Who can open a session in a team workspace: "team" = every workspace member,
|
|
19
|
+
* "private" = only its owner and people they invite (admins included only if invited).
|
|
20
|
+
*/
|
|
21
|
+
export type RoomVisibility = "team" | "private";
|
|
22
|
+
export declare function parseRoomVisibility(value: unknown): RoomVisibility;
|
|
23
|
+
/** Do workspace members get in without an invite? */
|
|
24
|
+
export declare function teamCanAccessRoom(row: {
|
|
25
|
+
org_id?: string | null;
|
|
26
|
+
visibility?: string | null;
|
|
27
|
+
}): boolean;
|
|
28
|
+
export declare function normalizeRoomRole(raw: string | null | undefined): RoomRole | null;
|
|
29
|
+
export declare function parseRoomInviteRole(raw: unknown, fallback?: RoomInviteRole): RoomInviteRole;
|
|
30
|
+
export declare function parseControlMode(raw: unknown, fallback?: ControlMode): ControlMode;
|
|
31
|
+
/** Default control mode for newly created rooms. Local agents are higher risk. */
|
|
32
|
+
export declare function defaultControlModeForRuntime(runtime: "local" | "cloud" | string): ControlMode;
|
|
33
|
+
export declare function roomRoleLabel(role: RoomRole): string;
|
|
34
|
+
export declare function roomRoleDescription(role: RoomRole): string;
|
|
35
|
+
export declare function controlModeLabel(mode: ControlMode): string;
|
|
36
|
+
export declare function controlModeDescription(mode: ControlMode): string;
|
|
37
|
+
export declare function canManageRoom(role: RoomRole | null | undefined): boolean;
|
|
38
|
+
export declare function canRequestDrive(role: RoomRole | null | undefined): boolean;
|
|
39
|
+
/** Viewers can read room memory; only editors and the host can mutate it. */
|
|
40
|
+
export declare function canEditMemory(role: RoomRole | null | undefined): boolean;
|
|
41
|
+
export declare function canSteerWithRole(opts: {
|
|
42
|
+
role: RoomRole | null | undefined;
|
|
43
|
+
controlMode: ControlMode;
|
|
44
|
+
isDrivingAgent: boolean;
|
|
45
|
+
}): boolean;
|
|
46
|
+
/** Abort follows the same gate as steering. */
|
|
47
|
+
export declare function canAbortWithRole(opts: {
|
|
48
|
+
role: RoomRole | null | undefined;
|
|
49
|
+
controlMode: ControlMode;
|
|
50
|
+
isDrivingAgent: boolean;
|
|
51
|
+
}): boolean;
|
|
52
|
+
export declare function steerDeniedReason(opts: {
|
|
53
|
+
role: RoomRole | null | undefined;
|
|
54
|
+
controlMode: ControlMode;
|
|
55
|
+
isDrivingAgent: boolean;
|
|
56
|
+
}): string | null;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Room collaboration permissions.
|
|
3
|
+
*
|
|
4
|
+
* Roles:
|
|
5
|
+
* - owner: room host
|
|
6
|
+
* - editor: can collaborate (subject to control mode)
|
|
7
|
+
* - viewer: read-only
|
|
8
|
+
*
|
|
9
|
+
* Control modes:
|
|
10
|
+
* - open: any editor can steer
|
|
11
|
+
* - driver: only the current driver (or host) can steer
|
|
12
|
+
* - host: only the host can steer
|
|
13
|
+
*/
|
|
14
|
+
export function parseRoomVisibility(value) {
|
|
15
|
+
return value === "private" ? "private" : "team";
|
|
16
|
+
}
|
|
17
|
+
/** Do workspace members get in without an invite? */
|
|
18
|
+
export function teamCanAccessRoom(row) {
|
|
19
|
+
return Boolean(row.org_id) && parseRoomVisibility(row.visibility) === "team";
|
|
20
|
+
}
|
|
21
|
+
/** Legacy room_members.role values still present in older DBs. */
|
|
22
|
+
const LEGACY_MEMBER_ALIASES = {
|
|
23
|
+
owner: "owner",
|
|
24
|
+
editor: "editor",
|
|
25
|
+
viewer: "viewer",
|
|
26
|
+
member: "editor",
|
|
27
|
+
admin: "editor",
|
|
28
|
+
};
|
|
29
|
+
export function normalizeRoomRole(raw) {
|
|
30
|
+
if (!raw)
|
|
31
|
+
return null;
|
|
32
|
+
return LEGACY_MEMBER_ALIASES[raw.trim().toLowerCase()] ?? null;
|
|
33
|
+
}
|
|
34
|
+
export function parseRoomInviteRole(raw, fallback = "viewer") {
|
|
35
|
+
if (typeof raw !== "string")
|
|
36
|
+
return fallback;
|
|
37
|
+
const n = raw.trim().toLowerCase();
|
|
38
|
+
if (n === "editor")
|
|
39
|
+
return "editor";
|
|
40
|
+
if (n === "viewer")
|
|
41
|
+
return "viewer";
|
|
42
|
+
// Legacy invite rows may lack a role; treat historical "member" as editor.
|
|
43
|
+
if (n === "member" || n === "admin")
|
|
44
|
+
return "editor";
|
|
45
|
+
return fallback;
|
|
46
|
+
}
|
|
47
|
+
export function parseControlMode(raw, fallback = "open") {
|
|
48
|
+
if (typeof raw !== "string")
|
|
49
|
+
return fallback;
|
|
50
|
+
const n = raw.trim().toLowerCase();
|
|
51
|
+
if (n === "open" || n === "driver" || n === "host")
|
|
52
|
+
return n;
|
|
53
|
+
return fallback;
|
|
54
|
+
}
|
|
55
|
+
/** Default control mode for newly created rooms. Local agents are higher risk. */
|
|
56
|
+
export function defaultControlModeForRuntime(runtime) {
|
|
57
|
+
return runtime === "local" ? "driver" : "open";
|
|
58
|
+
}
|
|
59
|
+
export function roomRoleLabel(role) {
|
|
60
|
+
if (role === "owner")
|
|
61
|
+
return "Host";
|
|
62
|
+
if (role === "editor")
|
|
63
|
+
return "Editor";
|
|
64
|
+
return "Viewer";
|
|
65
|
+
}
|
|
66
|
+
export function roomRoleDescription(role) {
|
|
67
|
+
if (role === "owner") {
|
|
68
|
+
return "Full control — manage agents, invites, and settings";
|
|
69
|
+
}
|
|
70
|
+
if (role === "editor") {
|
|
71
|
+
return "Can steer agents according to the room control mode";
|
|
72
|
+
}
|
|
73
|
+
return "Can watch chat, diffs, and presence — cannot steer";
|
|
74
|
+
}
|
|
75
|
+
export function controlModeLabel(mode) {
|
|
76
|
+
if (mode === "open")
|
|
77
|
+
return "Open collaboration";
|
|
78
|
+
if (mode === "driver")
|
|
79
|
+
return "Driver enforced";
|
|
80
|
+
return "Host only";
|
|
81
|
+
}
|
|
82
|
+
export function controlModeDescription(mode) {
|
|
83
|
+
if (mode === "open") {
|
|
84
|
+
return "Any editor can message agents";
|
|
85
|
+
}
|
|
86
|
+
if (mode === "driver") {
|
|
87
|
+
return "Only the current driver (or host) can message agents";
|
|
88
|
+
}
|
|
89
|
+
return "Only the host can message agents";
|
|
90
|
+
}
|
|
91
|
+
export function canManageRoom(role) {
|
|
92
|
+
return role === "owner";
|
|
93
|
+
}
|
|
94
|
+
export function canRequestDrive(role) {
|
|
95
|
+
return role === "owner" || role === "editor";
|
|
96
|
+
}
|
|
97
|
+
/** Viewers can read room memory; only editors and the host can mutate it. */
|
|
98
|
+
export function canEditMemory(role) {
|
|
99
|
+
return role === "owner" || role === "editor";
|
|
100
|
+
}
|
|
101
|
+
export function canSteerWithRole(opts) {
|
|
102
|
+
const { role, controlMode, isDrivingAgent } = opts;
|
|
103
|
+
if (!role)
|
|
104
|
+
return false;
|
|
105
|
+
if (role === "owner")
|
|
106
|
+
return true;
|
|
107
|
+
if (role === "viewer")
|
|
108
|
+
return false;
|
|
109
|
+
// editor
|
|
110
|
+
if (controlMode === "host")
|
|
111
|
+
return false;
|
|
112
|
+
if (controlMode === "open")
|
|
113
|
+
return true;
|
|
114
|
+
return isDrivingAgent;
|
|
115
|
+
}
|
|
116
|
+
/** Abort follows the same gate as steering. */
|
|
117
|
+
export function canAbortWithRole(opts) {
|
|
118
|
+
return canSteerWithRole(opts);
|
|
119
|
+
}
|
|
120
|
+
export function steerDeniedReason(opts) {
|
|
121
|
+
if (canSteerWithRole(opts))
|
|
122
|
+
return null;
|
|
123
|
+
if (!opts.role || opts.role === "viewer") {
|
|
124
|
+
return "Viewers cannot steer agents";
|
|
125
|
+
}
|
|
126
|
+
if (opts.controlMode === "host") {
|
|
127
|
+
return "Only the host can steer in host-only mode";
|
|
128
|
+
}
|
|
129
|
+
if (opts.controlMode === "driver" && !opts.isDrivingAgent) {
|
|
130
|
+
return "Request control to steer this agent";
|
|
131
|
+
}
|
|
132
|
+
return "You do not have permission to steer this agent";
|
|
133
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { AgentBackendKind } from "./backends/types.js";
|
|
2
|
+
import type { SessionSeat } from "./events.js";
|
|
3
|
+
/** One agent row, plus who is with it, before it becomes a SessionSeat. */
|
|
4
|
+
export interface SeatSourceAgent {
|
|
5
|
+
id: string;
|
|
6
|
+
backend: AgentBackendKind;
|
|
7
|
+
label: string;
|
|
8
|
+
createdBy?: string | null;
|
|
9
|
+
working: boolean;
|
|
10
|
+
/** Live driver, or the last person granted the driver seat. */
|
|
11
|
+
driverUserId?: string | null;
|
|
12
|
+
/** Person who steered the in-flight run. Preferred while working. */
|
|
13
|
+
steerer?: {
|
|
14
|
+
userId?: string;
|
|
15
|
+
name: string;
|
|
16
|
+
} | null;
|
|
17
|
+
/** Latest one-line focus from the agent's `update_focus` tool. */
|
|
18
|
+
focus?: string | null;
|
|
19
|
+
}
|
|
20
|
+
export interface SeatPerson {
|
|
21
|
+
userId: string;
|
|
22
|
+
name: string;
|
|
23
|
+
avatarUrl?: string;
|
|
24
|
+
}
|
|
25
|
+
/** First occurrence of each harness. Three Cursors and one Codex become two marks. */
|
|
26
|
+
export declare function uniqueAgentBackends(backends: readonly AgentBackendKind[] | undefined): AgentBackendKind[];
|
|
27
|
+
export declare function buildSessionSeats(agents: readonly SeatSourceAgent[], people: readonly SeatPerson[]): SessionSeat[];
|
|
28
|
+
/** Agents that are mid-run. Idle seats stay off the session row. */
|
|
29
|
+
export declare function workingSeats(seats: readonly SessionSeat[] | undefined): SessionSeat[];
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/** First occurrence of each harness. Three Cursors and one Codex become two marks. */
|
|
2
|
+
export function uniqueAgentBackends(backends) {
|
|
3
|
+
if (!backends?.length)
|
|
4
|
+
return [];
|
|
5
|
+
const seen = new Set();
|
|
6
|
+
const out = [];
|
|
7
|
+
for (const backend of backends) {
|
|
8
|
+
if (seen.has(backend))
|
|
9
|
+
continue;
|
|
10
|
+
seen.add(backend);
|
|
11
|
+
out.push(backend);
|
|
12
|
+
}
|
|
13
|
+
return out;
|
|
14
|
+
}
|
|
15
|
+
function personFor(agent, people) {
|
|
16
|
+
const steerer = agent.steerer?.name?.trim();
|
|
17
|
+
if (agent.working && steerer) {
|
|
18
|
+
const known = agent.steerer?.userId
|
|
19
|
+
? people.get(agent.steerer.userId)
|
|
20
|
+
: undefined;
|
|
21
|
+
return {
|
|
22
|
+
userId: agent.steerer?.userId || known?.userId,
|
|
23
|
+
name: steerer,
|
|
24
|
+
avatarUrl: known?.avatarUrl,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
const userId = agent.driverUserId || agent.createdBy || undefined;
|
|
28
|
+
const known = userId ? people.get(userId) : undefined;
|
|
29
|
+
if (known) {
|
|
30
|
+
return { userId: known.userId, name: known.name, avatarUrl: known.avatarUrl };
|
|
31
|
+
}
|
|
32
|
+
if (userId)
|
|
33
|
+
return { userId, name: "Teammate" };
|
|
34
|
+
return { name: agent.label };
|
|
35
|
+
}
|
|
36
|
+
export function buildSessionSeats(agents, people) {
|
|
37
|
+
const byId = new Map(people.map((person) => [person.userId, person]));
|
|
38
|
+
return agents.map((agent) => {
|
|
39
|
+
const person = personFor(agent, byId);
|
|
40
|
+
return {
|
|
41
|
+
agentId: agent.id,
|
|
42
|
+
backend: agent.backend,
|
|
43
|
+
agentLabel: agent.label,
|
|
44
|
+
working: agent.working,
|
|
45
|
+
userId: person.userId,
|
|
46
|
+
name: person.name,
|
|
47
|
+
avatarUrl: person.avatarUrl,
|
|
48
|
+
...(agent.focus?.trim() ? { focus: agent.focus.trim() } : {}),
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
/** Agents that are mid-run. Idle seats stay off the session row. */
|
|
53
|
+
export function workingSeats(seats) {
|
|
54
|
+
if (!seats?.length)
|
|
55
|
+
return [];
|
|
56
|
+
return seats.filter((seat) => seat.working);
|
|
57
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { AgentBackendKind } from "./backends/types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Slash commands typed into an agent's composer. `steer` commands are
|
|
4
|
+
* handled by Steer itself; `cli` commands go to the agent's CLI verbatim
|
|
5
|
+
* (Claude Code runs them locally in headless mode — `/context` costs no
|
|
6
|
+
* tokens). Imported by the web bundle, so no runtime imports here.
|
|
7
|
+
*/
|
|
8
|
+
export interface SlashCommandInfo {
|
|
9
|
+
name: string;
|
|
10
|
+
description: string;
|
|
11
|
+
handledBy: "steer" | "cli";
|
|
12
|
+
}
|
|
13
|
+
export type SteerCommandName = "clear" | "issue" | "status" | "ask" | "remember" | "integrate";
|
|
14
|
+
/** What Claude Code reports at startup (`system/init`), stored per agent. */
|
|
15
|
+
export interface CliCommandCatalog {
|
|
16
|
+
commands: string[];
|
|
17
|
+
/** Interactive-terminal-only commands (from the CLI itself). */
|
|
18
|
+
terminalOnly: string[];
|
|
19
|
+
skills: string[];
|
|
20
|
+
}
|
|
21
|
+
export declare function slashCommandsFor(backend: AgentBackendKind | undefined, catalog?: CliCommandCatalog | null): SlashCommandInfo[];
|
|
22
|
+
/** Menu matches: prefix matches first, then names containing the query. */
|
|
23
|
+
export declare function matchSlashCommands(commands: SlashCommandInfo[], query: string): SlashCommandInfo[];
|
|
24
|
+
export interface ParsedSlashCommand {
|
|
25
|
+
name: string;
|
|
26
|
+
/** Text after the command name (e.g. `/compact keep the API notes`). */
|
|
27
|
+
args: string;
|
|
28
|
+
}
|
|
29
|
+
/** `/name [args]` → parts; null for normal messages (and paths like `/src/x`). */
|
|
30
|
+
export declare function parseSlashCommand(text: string): ParsedSlashCommand | null;
|
|
31
|
+
export type SlashCommandAction = SteerCommandName | "passthrough" | "unsupported";
|
|
32
|
+
/**
|
|
33
|
+
* How a slash message for `backend` should be handled:
|
|
34
|
+
* - an Eigen command: Steer handles it in-process (see `SteerCommandName`)
|
|
35
|
+
* - `passthrough`: send the text to the CLI exactly as typed
|
|
36
|
+
* - `unsupported`: tell the user (the CLI can't run commands headless)
|
|
37
|
+
* - null: not a slash command for this agent — treat as a normal message
|
|
38
|
+
*/
|
|
39
|
+
export declare function slashCommandAction(backend: AgentBackendKind | undefined, text: string): SlashCommandAction | null;
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
const CLEAR = {
|
|
2
|
+
name: "clear",
|
|
3
|
+
description: "Start a new conversation (the agent forgets this chat)",
|
|
4
|
+
handledBy: "steer",
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Steer-owned commands. These run in-process (no CLI parsing, no model tokens
|
|
8
|
+
* unless the command explicitly asks the agent to act) and are offered for
|
|
9
|
+
* every backend.
|
|
10
|
+
*/
|
|
11
|
+
const STEER_COMMANDS = [
|
|
12
|
+
CLEAR,
|
|
13
|
+
{
|
|
14
|
+
name: "issue",
|
|
15
|
+
description: "File a Eigen Issue from this chat — the agent writes the title and description",
|
|
16
|
+
handledBy: "steer",
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
name: "status",
|
|
20
|
+
description: "Show this agent's backend, model, machine, clone path, branch, and PR",
|
|
21
|
+
handledBy: "steer",
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: "ask",
|
|
25
|
+
description: "Ask Keel about this workspace — cited answer from team memory (/ask <question>)",
|
|
26
|
+
handledBy: "steer",
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
name: "remember",
|
|
30
|
+
description: "Save a durable fact to team memory (/remember <fact>)",
|
|
31
|
+
handledBy: "steer",
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: "integrate",
|
|
35
|
+
description: "Merge this agent's branch into the session's integration branch",
|
|
36
|
+
handledBy: "steer",
|
|
37
|
+
},
|
|
38
|
+
];
|
|
39
|
+
const STEER_NAMES = new Set(STEER_COMMANDS.map((command) => command.name));
|
|
40
|
+
const CLAUDE_COMMANDS = [
|
|
41
|
+
CLEAR,
|
|
42
|
+
{ name: "context", description: "Show context window usage", handledBy: "cli" },
|
|
43
|
+
{ name: "compact", description: "Summarize the conversation to free up context", handledBy: "cli" },
|
|
44
|
+
];
|
|
45
|
+
/**
|
|
46
|
+
* Built-ins that work in the headless runs behind the chat (checked against
|
|
47
|
+
* `claude -p`): they print a result without needing the terminal UI.
|
|
48
|
+
*/
|
|
49
|
+
const CLAUDE_BUILTIN_ALLOWED = {
|
|
50
|
+
context: "Show context window usage",
|
|
51
|
+
compact: "Summarize the conversation to free up context",
|
|
52
|
+
cost: "Token usage and cost for this conversation",
|
|
53
|
+
usage: "Token usage and cost for this conversation",
|
|
54
|
+
recap: "Recap of the conversation so far",
|
|
55
|
+
mcp: "List the MCP servers and tools the agent has",
|
|
56
|
+
init: "Write a CLAUDE.md describing this repo",
|
|
57
|
+
review: "Review the current changes",
|
|
58
|
+
"security-review": "Security review of the pending changes",
|
|
59
|
+
"pr-comments": "Fetch comments from the GitHub pull request",
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Built-ins hidden from the chat: terminal UI / settings that wouldn't stick
|
|
63
|
+
* (every chat message is a fresh process — Eigen's own picker sets the
|
|
64
|
+
* model), or ones the CLI reports as unavailable headless.
|
|
65
|
+
*/
|
|
66
|
+
const CLAUDE_BUILTIN_HIDDEN = new Set([
|
|
67
|
+
"clear", "model", "effort", "fast", "config", "status", "memory", "hooks",
|
|
68
|
+
"permissions", "agents", "insights", "doctor", "color", "focus", "theme",
|
|
69
|
+
"vim", "output-style", "statusline", "terminal-setup", "ide", "add-dir",
|
|
70
|
+
"resume", "rewind", "rename", "export", "exit", "quit", "login", "logout",
|
|
71
|
+
"upgrade", "release-notes", "bug", "feedback", "privacy-settings",
|
|
72
|
+
"install-github-app", "reload-plugins", "reload-skills", "heapdump",
|
|
73
|
+
"auto-mode-setup", "autocompact", "plugin", "plugins", "skills", "tasks",
|
|
74
|
+
"loop", "goal", "advisor", "team-onboarding", "list-agents", "import",
|
|
75
|
+
"design-consent", "design-revoke", "workflow-launch-exec", "batch", "help",
|
|
76
|
+
]);
|
|
77
|
+
/**
|
|
78
|
+
* Menu entries for Claude Code: `/clear` (Eigen), allowed built-ins the CLI
|
|
79
|
+
* reports, then the repo's custom commands, skills and plugin commands.
|
|
80
|
+
* Without a catalog yet (agent hasn't run), the verified basics.
|
|
81
|
+
*/
|
|
82
|
+
function claudeCommandsFrom(catalog) {
|
|
83
|
+
if (!catalog?.commands?.length)
|
|
84
|
+
return CLAUDE_COMMANDS;
|
|
85
|
+
const terminalOnly = new Set(catalog.terminalOnly ?? []);
|
|
86
|
+
const skills = new Set(catalog.skills ?? []);
|
|
87
|
+
const builtins = [];
|
|
88
|
+
const custom = [];
|
|
89
|
+
const seen = new Set(["clear"]);
|
|
90
|
+
for (const raw of catalog.commands) {
|
|
91
|
+
const name = raw.replace(/^\//, "").trim();
|
|
92
|
+
if (!name || seen.has(name) || name.startsWith("_") || terminalOnly.has(name))
|
|
93
|
+
continue;
|
|
94
|
+
seen.add(name);
|
|
95
|
+
if (CLAUDE_BUILTIN_ALLOWED[name]) {
|
|
96
|
+
builtins.push({ name, description: CLAUDE_BUILTIN_ALLOWED[name], handledBy: "cli" });
|
|
97
|
+
}
|
|
98
|
+
else if (!CLAUDE_BUILTIN_HIDDEN.has(name)) {
|
|
99
|
+
custom.push({
|
|
100
|
+
name,
|
|
101
|
+
description: name.includes(":")
|
|
102
|
+
? "Plugin command"
|
|
103
|
+
: skills.has(name)
|
|
104
|
+
? "Skill"
|
|
105
|
+
: "Custom command",
|
|
106
|
+
handledBy: "cli",
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// Keep the two verified essentials even if a CLI version renames things.
|
|
111
|
+
for (const base of CLAUDE_COMMANDS) {
|
|
112
|
+
if (!seen.has(base.name) && base.name !== "clear")
|
|
113
|
+
builtins.push(base);
|
|
114
|
+
}
|
|
115
|
+
custom.sort((a, b) => a.name.localeCompare(b.name));
|
|
116
|
+
return [CLEAR, ...builtins, ...custom];
|
|
117
|
+
}
|
|
118
|
+
/** Steer commands first, then backend commands that don't shadow them. */
|
|
119
|
+
function withSteerCommands(commands) {
|
|
120
|
+
return [
|
|
121
|
+
...STEER_COMMANDS,
|
|
122
|
+
...commands.filter((command) => !STEER_NAMES.has(command.name)),
|
|
123
|
+
];
|
|
124
|
+
}
|
|
125
|
+
export function slashCommandsFor(backend, catalog) {
|
|
126
|
+
if (backend === "claude-code")
|
|
127
|
+
return withSteerCommands(claudeCommandsFrom(catalog));
|
|
128
|
+
return [...STEER_COMMANDS];
|
|
129
|
+
}
|
|
130
|
+
/** Menu matches: prefix matches first, then names containing the query. */
|
|
131
|
+
export function matchSlashCommands(commands, query) {
|
|
132
|
+
const q = query.toLowerCase();
|
|
133
|
+
const prefix = commands.filter((c) => c.name.startsWith(q));
|
|
134
|
+
const inner = commands.filter((c) => !c.name.startsWith(q) && c.name.includes(q));
|
|
135
|
+
return [...prefix, ...inner];
|
|
136
|
+
}
|
|
137
|
+
/** `/name [args]` → parts; null for normal messages (and paths like `/src/x`). */
|
|
138
|
+
export function parseSlashCommand(text) {
|
|
139
|
+
const match = /^\/([a-z][\w:-]*)(?:\s+([\s\S]*))?$/i.exec(text.trim());
|
|
140
|
+
if (!match)
|
|
141
|
+
return null;
|
|
142
|
+
return { name: match[1].toLowerCase(), args: (match[2] ?? "").trim() };
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* How a slash message for `backend` should be handled:
|
|
146
|
+
* - an Eigen command: Steer handles it in-process (see `SteerCommandName`)
|
|
147
|
+
* - `passthrough`: send the text to the CLI exactly as typed
|
|
148
|
+
* - `unsupported`: tell the user (the CLI can't run commands headless)
|
|
149
|
+
* - null: not a slash command for this agent — treat as a normal message
|
|
150
|
+
*/
|
|
151
|
+
export function slashCommandAction(backend, text) {
|
|
152
|
+
const parsed = parseSlashCommand(text);
|
|
153
|
+
if (!parsed)
|
|
154
|
+
return null;
|
|
155
|
+
if (STEER_NAMES.has(parsed.name)) {
|
|
156
|
+
return parsed.name;
|
|
157
|
+
}
|
|
158
|
+
if (backend === "claude-code") {
|
|
159
|
+
// Claude Code also runs project/user custom commands and skills.
|
|
160
|
+
return "passthrough";
|
|
161
|
+
}
|
|
162
|
+
if (backend === "codex" || backend === "opencode") {
|
|
163
|
+
return "unsupported";
|
|
164
|
+
}
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Space metadata carried on chat messages (`ChatMessage.space`).
|
|
3
|
+
*
|
|
4
|
+
* Stored as one JSON column so new Space features (reactions, pins, …) can
|
|
5
|
+
* add fields here without another schema migration. Keep this file free of
|
|
6
|
+
* runtime imports — the web bundler can't resolve "./x.js" between shared/
|
|
7
|
+
* files.
|
|
8
|
+
*/
|
|
9
|
+
export interface SpaceHandoffRef {
|
|
10
|
+
/** Agent whose reply is being handed off. */
|
|
11
|
+
agentId: string;
|
|
12
|
+
/** The assistant message that was handed off. */
|
|
13
|
+
messageId: string;
|
|
14
|
+
}
|
|
15
|
+
export interface SpaceMessageMeta {
|
|
16
|
+
/**
|
|
17
|
+
* Where the message was composed: the room-wide Space, or another agent's
|
|
18
|
+
* chat (a hand-off sent by tagging one agent from that chat).
|
|
19
|
+
*/
|
|
20
|
+
origin?: "space" | "agent";
|
|
21
|
+
/** Root message id of the thread this message belongs to. */
|
|
22
|
+
threadId?: string;
|
|
23
|
+
/** Another agent's reply passed along as context. */
|
|
24
|
+
handoff?: SpaceHandoffRef;
|
|
25
|
+
/** People @mentioned in the message. */
|
|
26
|
+
mentionUserIds?: string[];
|
|
27
|
+
/** Thread root only: nothing in this thread goes into team memory. */
|
|
28
|
+
offRecord?: boolean;
|
|
29
|
+
}
|
|
30
|
+
/** Validate untrusted client input; drops anything malformed. */
|
|
31
|
+
export declare function sanitizeSpaceMeta(input: unknown): SpaceMessageMeta | undefined;
|
|
32
|
+
export declare function parseSpaceMetaJson(raw: unknown): SpaceMessageMeta | undefined;
|
|
33
|
+
export declare function spaceMetaJson(meta: SpaceMessageMeta | undefined): string | null;
|