@ctliz/agent-intercom-pi 0.11.0-connect.2 → 0.12.0-connect.1
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/index.ts +57 -10
- package/package.json +2 -2
- package/team.ts +188 -50
package/index.ts
CHANGED
|
@@ -487,17 +487,50 @@ function parseSubagentIntercomPayload(payload: unknown): { to: string; message:
|
|
|
487
487
|
const requestId = typeof record.requestId === "string" ? record.requestId : undefined;
|
|
488
488
|
return { to: record.to, message: record.message, ...(requestId ? { requestId } : {}) };
|
|
489
489
|
}
|
|
490
|
-
|
|
490
|
+
|
|
491
|
+
const GENERIC_SESSION_ID_PATTERN = /^[A-Za-z0-9_-]{1,128}$/;
|
|
492
|
+
|
|
493
|
+
export function resolveIntercomSessionId(
|
|
494
|
+
fallbackSessionId: string,
|
|
495
|
+
environment: NodeJS.ProcessEnv = process.env,
|
|
496
|
+
): string {
|
|
497
|
+
const harnessSessionId = environment[INTERCOM_SESSION_ID_ENV]?.trim();
|
|
498
|
+
if (harnessSessionId) {
|
|
499
|
+
return harnessSessionId;
|
|
500
|
+
}
|
|
501
|
+
const genericSessionId = environment.AGENT_INTERCOM_SESSION_ID?.trim();
|
|
502
|
+
if (genericSessionId) {
|
|
503
|
+
if (!GENERIC_SESSION_ID_PATTERN.test(genericSessionId)) {
|
|
504
|
+
throw new Error("Invalid AGENT_INTERCOM_SESSION_ID: must match ^[A-Za-z0-9_-]{1,128}$");
|
|
505
|
+
}
|
|
506
|
+
return genericSessionId;
|
|
507
|
+
}
|
|
508
|
+
return fallbackSessionId;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
export function resolveIntercomPresenceName(
|
|
512
|
+
sessionName: string | undefined,
|
|
513
|
+
sessionId: string,
|
|
514
|
+
environment: NodeJS.ProcessEnv = process.env,
|
|
515
|
+
): string {
|
|
491
516
|
const trimmedName = sessionName?.trim();
|
|
492
517
|
if (trimmedName) {
|
|
493
518
|
return trimmedName;
|
|
494
519
|
}
|
|
520
|
+
const subagentName = environment[SUBAGENT_INTERCOM_SESSION_NAME_ENV]?.trim();
|
|
521
|
+
if (subagentName) {
|
|
522
|
+
return subagentName;
|
|
523
|
+
}
|
|
524
|
+
const genericName = environment.AGENT_INTERCOM_SESSION_NAME?.trim();
|
|
525
|
+
if (genericName) {
|
|
526
|
+
return genericName;
|
|
527
|
+
}
|
|
495
528
|
const normalizedSessionId = sessionId.startsWith("session-") ? sessionId.slice("session-".length) : sessionId;
|
|
496
529
|
return `${DEFAULT_UNNAMED_SESSION_ALIAS_PREFIX}-${normalizedSessionId.slice(0, 8)}`;
|
|
497
530
|
}
|
|
498
|
-
function buildPresenceIdentity(pi: ExtensionAPI, sessionId: string): { name: string } {
|
|
531
|
+
function buildPresenceIdentity(pi: ExtensionAPI, sessionId: string, environment: NodeJS.ProcessEnv = process.env): { name: string } {
|
|
499
532
|
return {
|
|
500
|
-
name: resolveIntercomPresenceName(pi.getSessionName(), sessionId),
|
|
533
|
+
name: resolveIntercomPresenceName(pi.getSessionName(), sessionId, environment),
|
|
501
534
|
};
|
|
502
535
|
}
|
|
503
536
|
function formatSessionLabel(session: SessionInfo, duplicates: Set<string>, idPrefix: string): string {
|
|
@@ -623,6 +656,14 @@ function getNamePollMs(): number {
|
|
|
623
656
|
}
|
|
624
657
|
export default function piIntercomExtension(pi: ExtensionAPI) {
|
|
625
658
|
const runtimeScopeId = intercomScopeIdFromEnvForRegistration();
|
|
659
|
+
const initialHarnessSessionId = process.env[INTERCOM_SESSION_ID_ENV]?.trim();
|
|
660
|
+
const initialGenericSessionId = process.env.AGENT_INTERCOM_SESSION_ID?.trim();
|
|
661
|
+
if (initialGenericSessionId && !initialHarnessSessionId) {
|
|
662
|
+
if (!GENERIC_SESSION_ID_PATTERN.test(initialGenericSessionId)) {
|
|
663
|
+
throw new Error("Invalid AGENT_INTERCOM_SESSION_ID: must match ^[A-Za-z0-9_-]{1,128}$");
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
const configuredSessionId = initialHarnessSessionId || initialGenericSessionId;
|
|
626
667
|
let client: IntercomClient | null = null;
|
|
627
668
|
const config: IntercomConfig = loadConfig();
|
|
628
669
|
const bossTeamScope = readBossTeamScope();
|
|
@@ -739,8 +780,11 @@ export default function piIntercomExtension(pi: ExtensionAPI) {
|
|
|
739
780
|
return null;
|
|
740
781
|
}
|
|
741
782
|
try {
|
|
742
|
-
if (currentSessionId
|
|
743
|
-
|
|
783
|
+
if (currentSessionId) {
|
|
784
|
+
const expectedSessionId = configuredSessionId || ctx.sessionManager.getSessionId();
|
|
785
|
+
if (expectedSessionId !== currentSessionId) {
|
|
786
|
+
return null;
|
|
787
|
+
}
|
|
744
788
|
}
|
|
745
789
|
void ctx.hasUI;
|
|
746
790
|
return ctx;
|
|
@@ -789,11 +833,12 @@ export default function piIntercomExtension(pi: ExtensionAPI) {
|
|
|
789
833
|
...(runtimeInstanceId ? { runtimeInstanceId } : {}),
|
|
790
834
|
};
|
|
791
835
|
}
|
|
792
|
-
function syncPresenceIdentity(sessionId
|
|
836
|
+
function syncPresenceIdentity(sessionId?: string): void {
|
|
793
837
|
if (!client || !getLiveContext()) {
|
|
794
838
|
return;
|
|
795
839
|
}
|
|
796
|
-
const
|
|
840
|
+
const resolvedId = currentSessionId ?? (configuredSessionId || (sessionId ? sessionId : "default"));
|
|
841
|
+
const identity = buildPresenceIdentity(pi, resolvedId);
|
|
797
842
|
lastPresenceName = identity.name;
|
|
798
843
|
client.updatePresence({ ...identity, status: currentStatus() });
|
|
799
844
|
}
|
|
@@ -1343,7 +1388,8 @@ export default function piIntercomExtension(pi: ExtensionAPI) {
|
|
|
1343
1388
|
rejectAllReplyWaiters(new Error("Session replaced"));
|
|
1344
1389
|
replyTracker.reset();
|
|
1345
1390
|
runtimeContext = ctx;
|
|
1346
|
-
currentSessionId = ctx.sessionManager.getSessionId();
|
|
1391
|
+
currentSessionId = configuredSessionId || ctx.sessionManager.getSessionId();
|
|
1392
|
+
publishIntercomSessionId(currentSessionId);
|
|
1347
1393
|
if (bossTeamScope.present && bossSelfSessionError(bossTeamScope, currentSessionId)) {
|
|
1348
1394
|
const staleOutbox = new PersistentOutboundOutbox(currentSessionId);
|
|
1349
1395
|
const removed = staleOutbox.list().length;
|
|
@@ -1666,7 +1712,8 @@ export default function piIntercomExtension(pi: ExtensionAPI) {
|
|
|
1666
1712
|
scheduleInboundFlush(0);
|
|
1667
1713
|
});
|
|
1668
1714
|
pi.on("turn_start", (_event, ctx) => {
|
|
1669
|
-
const
|
|
1715
|
+
const rawSessionId = ctx.sessionManager.getSessionId();
|
|
1716
|
+
const sessionId = configuredSessionId || rawSessionId;
|
|
1670
1717
|
if (!currentSessionId || sessionId !== currentSessionId) {
|
|
1671
1718
|
if (!config.enabled) {
|
|
1672
1719
|
return;
|
|
@@ -1688,7 +1735,7 @@ export default function piIntercomExtension(pi: ExtensionAPI) {
|
|
|
1688
1735
|
currentModel = event.model.id;
|
|
1689
1736
|
if (client) {
|
|
1690
1737
|
client.updatePresence({
|
|
1691
|
-
...buildPresenceIdentity(pi, ctx.sessionManager.getSessionId()),
|
|
1738
|
+
...buildPresenceIdentity(pi, currentSessionId ?? (configuredSessionId || ctx.sessionManager.getSessionId())),
|
|
1692
1739
|
model: event.model.id,
|
|
1693
1740
|
status: currentStatus(),
|
|
1694
1741
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ctliz/agent-intercom-pi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0-connect.1",
|
|
4
4
|
"license": "AGPL-3.0-or-later",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
]
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@ctliz/agent-intercom-core": "
|
|
47
|
+
"@ctliz/agent-intercom-core": "0.2.0",
|
|
48
48
|
"@earendil-works/pi-ai": "*",
|
|
49
49
|
"@earendil-works/pi-coding-agent": "*",
|
|
50
50
|
"@earendil-works/pi-tui": "*",
|
package/team.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
+
import { readTeamManifest, TeamManifestError } from "@ctliz/agent-intercom-core/team-manifest";
|
|
3
4
|
import { getAgentDirPath } from "./broker/paths.ts";
|
|
4
5
|
import { bossSelfSessionError, resolveBossLiveSession, type BossTeamScope } from "./boss-team-scope.ts";
|
|
5
6
|
|
|
6
7
|
export interface TeamSession {
|
|
7
8
|
id: string;
|
|
8
9
|
name?: string;
|
|
10
|
+
model?: string;
|
|
9
11
|
origin?: "local" | "remote";
|
|
10
12
|
}
|
|
11
13
|
|
|
@@ -29,9 +31,12 @@ export interface TeamMember {
|
|
|
29
31
|
connected: boolean;
|
|
30
32
|
}
|
|
31
33
|
|
|
34
|
+
export type IntercomTeamSource = "orchestrator" | "manifest" | "live_roster" | "standalone";
|
|
35
|
+
|
|
32
36
|
export interface IntercomTeam {
|
|
37
|
+
source: IntercomTeamSource;
|
|
33
38
|
teamId?: string;
|
|
34
|
-
self: { id: string; workerId?: string; isManager: boolean };
|
|
39
|
+
self: { id: string; workerId?: string; isManager: boolean; role?: string };
|
|
35
40
|
manager?: { target: string; connected: boolean };
|
|
36
41
|
controller?: { target: string; connected: boolean };
|
|
37
42
|
coworkers: TeamMember[];
|
|
@@ -46,6 +51,9 @@ export function resolveManagedInboxSession(input: {
|
|
|
46
51
|
if (!input.team.self.isManager) {
|
|
47
52
|
throw new Error("Only a manager may inspect another session's pending-ask inbox");
|
|
48
53
|
}
|
|
54
|
+
if (input.team.source !== "orchestrator") {
|
|
55
|
+
throw new Error("Pending-ask inbox inspection is only available for orchestrator-managed teams");
|
|
56
|
+
}
|
|
49
57
|
const member = input.team.coworkers.find((entry) => entry.target === input.requestedSession);
|
|
50
58
|
if (!member) {
|
|
51
59
|
throw new Error(`Pending-ask inbox access denied for "${input.requestedSession}"; select an owned coworker target returned by intercom_team`);
|
|
@@ -80,6 +88,13 @@ async function readWorkers(agentDir: string): Promise<StoredWorker[]> {
|
|
|
80
88
|
}
|
|
81
89
|
}
|
|
82
90
|
|
|
91
|
+
/**
|
|
92
|
+
* Resolves the Intercom team following the exact priority hierarchy:
|
|
93
|
+
* 1. Authoritative Orchestrator record in workers.json (matching AGENT_INTERCOM_WORKER_ID or active manager ownership)
|
|
94
|
+
* 2. Explicit TmuxDeck Manifest (AGENT_INTERCOM_TEAM_MANIFEST)
|
|
95
|
+
* 3. Workspace Live Same-Scope Non-Human Roster Fallback (AGENT_INTERCOM_SCOPE_ID present)
|
|
96
|
+
* 4. Standalone unmanaged session
|
|
97
|
+
*/
|
|
83
98
|
export async function resolveIntercomTeam(input: {
|
|
84
99
|
selfId: string;
|
|
85
100
|
sessions: TeamSession[];
|
|
@@ -93,39 +108,184 @@ export async function resolveIntercomTeam(input: {
|
|
|
93
108
|
const current = workerId
|
|
94
109
|
? workers.find((worker) => stringValue(worker.id) === workerId && (!runId || stringValue(worker.runId) === runId))
|
|
95
110
|
: undefined;
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
111
|
+
|
|
112
|
+
// 1. Authoritative Orchestrator Record
|
|
113
|
+
if (current) {
|
|
114
|
+
const managerTarget =
|
|
115
|
+
stringValue(current.managerSessionId) ??
|
|
116
|
+
stringValue(env.AGENT_INTERCOM_MANAGER_TARGET) ??
|
|
117
|
+
stringValue(env.AGENT_INTERCOM_MANAGER_SESSION_ID);
|
|
118
|
+
|
|
119
|
+
const coworkers: TeamMember[] = managerTarget
|
|
120
|
+
? workers
|
|
121
|
+
.filter((worker) => worker.owned === true)
|
|
122
|
+
.filter((worker) => stringValue(worker.managerSessionId) === managerTarget)
|
|
123
|
+
.filter((worker) => LIVE_STATES.has(stringValue(worker.state) ?? ""))
|
|
124
|
+
.filter((worker) => stringValue(worker.id) !== workerId)
|
|
125
|
+
.map((worker): TeamMember | undefined => {
|
|
126
|
+
const id = stringValue(worker.id);
|
|
127
|
+
if (!id) return undefined;
|
|
128
|
+
const target = stringValue(worker.intercomTarget) ?? id;
|
|
129
|
+
return {
|
|
130
|
+
id,
|
|
131
|
+
target,
|
|
132
|
+
...(stringValue(worker.harness) ? { harness: stringValue(worker.harness) } : {}),
|
|
133
|
+
...(stringValue(worker.role) ? { role: stringValue(worker.role) } : {}),
|
|
134
|
+
...(stringValue(worker.state) ? { state: stringValue(worker.state) } : {}),
|
|
135
|
+
connected: connectedTo(input.sessions, target),
|
|
136
|
+
};
|
|
137
|
+
})
|
|
138
|
+
.filter((member): member is TeamMember => Boolean(member))
|
|
139
|
+
: [];
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
source: "orchestrator",
|
|
143
|
+
...(managerTarget ? { teamId: managerTarget } : {}),
|
|
144
|
+
self: { id: input.selfId, ...(workerId ? { workerId } : {}), isManager: false },
|
|
145
|
+
...(managerTarget
|
|
146
|
+
? { manager: { target: managerTarget, connected: connectedTo(input.sessions, managerTarget) } }
|
|
147
|
+
: {}),
|
|
148
|
+
coworkers,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (workerId === undefined && input.selfId) {
|
|
153
|
+
const managerOwnedCoworkers = workers
|
|
154
|
+
.filter((worker) => worker.owned === true)
|
|
155
|
+
.filter((worker) => stringValue(worker.managerSessionId) === input.selfId)
|
|
156
|
+
.filter((worker) => LIVE_STATES.has(stringValue(worker.state) ?? ""))
|
|
157
|
+
.map((worker): TeamMember | undefined => {
|
|
158
|
+
const id = stringValue(worker.id);
|
|
159
|
+
if (!id) return undefined;
|
|
160
|
+
const target = stringValue(worker.intercomTarget) ?? id;
|
|
161
|
+
return {
|
|
162
|
+
id,
|
|
163
|
+
target,
|
|
164
|
+
...(stringValue(worker.harness) ? { harness: stringValue(worker.harness) } : {}),
|
|
165
|
+
...(stringValue(worker.role) ? { role: stringValue(worker.role) } : {}),
|
|
166
|
+
...(stringValue(worker.state) ? { state: stringValue(worker.state) } : {}),
|
|
167
|
+
connected: connectedTo(input.sessions, target),
|
|
168
|
+
};
|
|
169
|
+
})
|
|
170
|
+
.filter((member): member is TeamMember => Boolean(member));
|
|
171
|
+
|
|
172
|
+
if (managerOwnedCoworkers.length > 0) {
|
|
108
173
|
return {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
connected: connectedTo(input.sessions, target),
|
|
174
|
+
source: "orchestrator",
|
|
175
|
+
teamId: input.selfId,
|
|
176
|
+
self: { id: input.selfId, isManager: true },
|
|
177
|
+
manager: { target: input.selfId, connected: true },
|
|
178
|
+
coworkers: managerOwnedCoworkers,
|
|
115
179
|
};
|
|
116
|
-
}
|
|
117
|
-
|
|
180
|
+
}
|
|
181
|
+
}
|
|
118
182
|
|
|
183
|
+
// 2. Explicit TmuxDeck Team Manifest
|
|
184
|
+
if (env.AGENT_INTERCOM_TEAM_MANIFEST !== undefined) {
|
|
185
|
+
const rawManifestPath = env.AGENT_INTERCOM_TEAM_MANIFEST;
|
|
186
|
+
if (typeof rawManifestPath !== "string" || !rawManifestPath.trim()) {
|
|
187
|
+
throw new TeamManifestError("ERR_TEAM_MANIFEST_INVALID");
|
|
188
|
+
}
|
|
189
|
+
const manifest = readTeamManifest(rawManifestPath.trim());
|
|
190
|
+
if (!manifest) {
|
|
191
|
+
throw new TeamManifestError("ERR_TEAM_MANIFEST_INVALID");
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const selfMember = manifest.members.find((member) => member.sessionId === input.selfId);
|
|
195
|
+
if (!selfMember) {
|
|
196
|
+
throw new TeamManifestError("ERR_TEAM_MANIFEST_INVALID");
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const isManager = input.selfId === manifest.leadId;
|
|
200
|
+
const managerTarget = manifest.leadId;
|
|
201
|
+
|
|
202
|
+
// Coworkers:
|
|
203
|
+
// Lead: all workers
|
|
204
|
+
// Worker: other workers (excluding self and excluding Lead/manager)
|
|
205
|
+
const coworkers: TeamMember[] = manifest.members
|
|
206
|
+
.filter((member) => member.sessionId !== input.selfId && member.sessionId !== manifest.leadId)
|
|
207
|
+
.map((member) => ({
|
|
208
|
+
id: member.sessionId,
|
|
209
|
+
target: member.sessionId,
|
|
210
|
+
role: member.role,
|
|
211
|
+
connected: connectedTo(input.sessions, member.sessionId),
|
|
212
|
+
}));
|
|
213
|
+
|
|
214
|
+
return {
|
|
215
|
+
source: "manifest",
|
|
216
|
+
teamId: manifest.runId,
|
|
217
|
+
self: {
|
|
218
|
+
id: input.selfId,
|
|
219
|
+
isManager,
|
|
220
|
+
role: selfMember.role,
|
|
221
|
+
},
|
|
222
|
+
manager: { target: managerTarget, connected: connectedTo(input.sessions, managerTarget) },
|
|
223
|
+
coworkers,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// 3. Workspace Live Same-Scope Non-Human Roster Fallback
|
|
228
|
+
const scopeId = stringValue(env.AGENT_INTERCOM_SCOPE_ID);
|
|
229
|
+
if (scopeId !== undefined) {
|
|
230
|
+
const managerTarget =
|
|
231
|
+
stringValue(env.AGENT_INTERCOM_MANAGER_TARGET) ??
|
|
232
|
+
stringValue(env.AGENT_INTERCOM_MANAGER_SESSION_ID);
|
|
233
|
+
const isManager = !managerTarget || managerTarget === input.selfId;
|
|
234
|
+
const effectiveManagerTarget = isManager ? input.selfId : managerTarget;
|
|
235
|
+
|
|
236
|
+
const coworkers: TeamMember[] = input.sessions
|
|
237
|
+
.filter((session) => session.id !== input.selfId)
|
|
238
|
+
.filter((session) => session.model !== "human")
|
|
239
|
+
.filter((session) => isManager || session.id !== effectiveManagerTarget)
|
|
240
|
+
.map((session) => ({
|
|
241
|
+
id: session.id,
|
|
242
|
+
target: session.id,
|
|
243
|
+
connected: true,
|
|
244
|
+
}));
|
|
245
|
+
|
|
246
|
+
return {
|
|
247
|
+
source: "live_roster",
|
|
248
|
+
teamId: effectiveManagerTarget,
|
|
249
|
+
self: { id: input.selfId, isManager },
|
|
250
|
+
manager: {
|
|
251
|
+
target: effectiveManagerTarget,
|
|
252
|
+
connected: connectedTo(input.sessions, effectiveManagerTarget),
|
|
253
|
+
},
|
|
254
|
+
coworkers,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// 4. Standalone Session Fallback
|
|
119
259
|
return {
|
|
120
|
-
|
|
121
|
-
self: { id: input.selfId,
|
|
122
|
-
|
|
123
|
-
? { target: managerTarget, connected: connectedTo(input.sessions, managerTarget) }
|
|
124
|
-
: { target: input.selfId, connected: true },
|
|
125
|
-
coworkers,
|
|
260
|
+
source: "standalone",
|
|
261
|
+
self: { id: input.selfId, isManager: false },
|
|
262
|
+
coworkers: [],
|
|
126
263
|
};
|
|
127
264
|
}
|
|
128
265
|
|
|
266
|
+
export function formatIntercomTeam(team: IntercomTeam): string {
|
|
267
|
+
const manager = team.manager
|
|
268
|
+
? `${team.manager.target} [${team.manager.connected ? "connected" : "not connected"}]`
|
|
269
|
+
: "unknown";
|
|
270
|
+
const lines = [
|
|
271
|
+
`Manager: ${manager}`,
|
|
272
|
+
`You: ${team.self.id}${team.self.isManager ? " [manager]" : ""}`,
|
|
273
|
+
];
|
|
274
|
+
if (team.controller) {
|
|
275
|
+
lines.push(`Controller: ${team.controller.target} [connected]`);
|
|
276
|
+
}
|
|
277
|
+
if (team.coworkers.length === 0) {
|
|
278
|
+
lines.push("Coworkers: none");
|
|
279
|
+
} else {
|
|
280
|
+
lines.push("Coworkers:");
|
|
281
|
+
for (const coworker of team.coworkers) {
|
|
282
|
+
const metadata = [coworker.harness, coworker.role, coworker.state].filter(Boolean).join(", ");
|
|
283
|
+
lines.push(`- ${coworker.id} target=${coworker.target}${metadata ? ` (${metadata})` : ""} [${coworker.connected ? "connected" : "not connected"}]`);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return lines.join("\n");
|
|
287
|
+
}
|
|
288
|
+
|
|
129
289
|
export function resolveBossIntercomTeam(input: {
|
|
130
290
|
selfId: string;
|
|
131
291
|
sessions: TeamSession[];
|
|
@@ -160,6 +320,7 @@ export function resolveBossIntercomTeam(input: {
|
|
|
160
320
|
}
|
|
161
321
|
|
|
162
322
|
return {
|
|
323
|
+
source: "orchestrator",
|
|
163
324
|
...(manager ? { teamId: manager.target } : {}),
|
|
164
325
|
self: { id: input.selfId, isManager },
|
|
165
326
|
...(manager ? { manager } : {}),
|
|
@@ -167,26 +328,3 @@ export function resolveBossIntercomTeam(input: {
|
|
|
167
328
|
coworkers,
|
|
168
329
|
};
|
|
169
330
|
}
|
|
170
|
-
|
|
171
|
-
export function formatIntercomTeam(team: IntercomTeam): string {
|
|
172
|
-
const manager = team.manager
|
|
173
|
-
? `${team.manager.target} [${team.manager.connected ? "connected" : "not connected"}]`
|
|
174
|
-
: "unknown";
|
|
175
|
-
const lines = [
|
|
176
|
-
`Manager: ${manager}`,
|
|
177
|
-
`You: ${team.self.id}${team.self.isManager ? " [manager]" : ""}`,
|
|
178
|
-
];
|
|
179
|
-
if (team.controller) {
|
|
180
|
-
lines.push(`Controller: ${team.controller.target} [connected]`);
|
|
181
|
-
}
|
|
182
|
-
if (team.coworkers.length === 0) {
|
|
183
|
-
lines.push("Coworkers: none");
|
|
184
|
-
} else {
|
|
185
|
-
lines.push("Coworkers:");
|
|
186
|
-
for (const coworker of team.coworkers) {
|
|
187
|
-
const metadata = [coworker.harness, coworker.role, coworker.state].filter(Boolean).join(", ");
|
|
188
|
-
lines.push(`- ${coworker.id} target=${coworker.target}${metadata ? ` (${metadata})` : ""} [${coworker.connected ? "connected" : "not connected"}]`);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
return lines.join("\n");
|
|
192
|
-
}
|