@ctliz/agent-intercom-codex 0.11.0-connect.2
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/.codex-plugin/plugin.json +39 -0
- package/.mcp.json +10 -0
- package/COPYRIGHT +6 -0
- package/LICENSE +661 -0
- package/LICENSE_TRANSITION.md +25 -0
- package/README.md +572 -0
- package/THIRD_PARTY_NOTICES.md +17 -0
- package/assets/logo-generated.png +0 -0
- package/assets/logo.svg +22 -0
- package/boss-control-outbox.ts +195 -0
- package/broker/access-credential.ts +76 -0
- package/broker/access-registry.ts +464 -0
- package/broker/audit.ts +60 -0
- package/broker/authorization.ts +54 -0
- package/broker/boss-adapter.ts +531 -0
- package/broker/boss-control-ledger.ts +306 -0
- package/broker/broker.ts +2327 -0
- package/broker/client.ts +1111 -0
- package/broker/framing.ts +87 -0
- package/broker/ownership.ts +61 -0
- package/broker/paths.ts +161 -0
- package/broker/spawn.ts +453 -0
- package/codex/app-server-client.ts +562 -0
- package/codex/boss-client.ts +34 -0
- package/codex/bridge-config.ts +282 -0
- package/codex/bridge-daemon.ts +913 -0
- package/codex/clipboard.ts +142 -0
- package/codex/coi.ts +802 -0
- package/codex/contact.ts +54 -0
- package/codex/mcp-protocol.ts +248 -0
- package/codex/runtime.ts +488 -0
- package/codex/server.ts +60 -0
- package/codex/team.ts +249 -0
- package/codex/tui-input.ts +174 -0
- package/config.ts +195 -0
- package/dist/bridge-daemon.mjs +3781 -0
- package/dist/broker.mjs +3012 -0
- package/dist/build-info.json +12 -0
- package/dist/codex-server.mjs +2890 -0
- package/dist/coi.mjs +4686 -0
- package/durable-json.ts +25 -0
- package/licenses/MIT-pi-intercom.txt +21 -0
- package/outbound-outbox.ts +116 -0
- package/package.json +89 -0
- package/protocol-v4/contract.ts +27 -0
- package/provider/protected-service.ts +162 -0
- package/provider/provider.mjs +34 -0
- package/scripts/build-info.mjs +58 -0
- package/scripts/build.mjs +55 -0
- package/skills/codex-intercom/SKILL.md +104 -0
- package/tsconfig.json +16 -0
- package/types.ts +225 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: codex-intercom
|
|
3
|
+
description: |
|
|
4
|
+
Coordinate with local Pi or Codex sessions through pi-intercom MCP tools.
|
|
5
|
+
Use for same-machine planner-worker workflows, direct peer questions,
|
|
6
|
+
cross-session context sharing, and checking or replying to pending asks.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Codex Intercom
|
|
10
|
+
|
|
11
|
+
Use this skill when you need to coordinate with another local coding-agent
|
|
12
|
+
session through the `pi-intercom` MCP server.
|
|
13
|
+
|
|
14
|
+
Codex cannot currently receive unsolicited MCP messages as a fresh visible turn.
|
|
15
|
+
Inbound messages are queued by the MCP server while it is running. Check
|
|
16
|
+
`intercom_pending` at natural boundaries, before starting delegated work, and
|
|
17
|
+
when you expect a response.
|
|
18
|
+
|
|
19
|
+
If the user needs wake-on-message behavior, use or recommend the app-server
|
|
20
|
+
bridge daemon. It exposes configured virtual Codex workers as intercom sessions;
|
|
21
|
+
messages to those workers create or resume app-server threads and start turns.
|
|
22
|
+
|
|
23
|
+
## Tools
|
|
24
|
+
|
|
25
|
+
- `intercom_whoami`: show this session's intercom ID, name, cwd, and model.
|
|
26
|
+
- `intercom_team`: show the current orchestrator manager and live same-manager coworkers.
|
|
27
|
+
- `intercom_status`: check connection, active session count, unread messages,
|
|
28
|
+
and unresolved inbound asks.
|
|
29
|
+
- `intercom_list`: list connected Pi and Codex sessions.
|
|
30
|
+
- `intercom_set_summary`: publish a short discoverable status.
|
|
31
|
+
- `intercom_send`: fire-and-forget direct message.
|
|
32
|
+
- `intercom_ask`: send a question and wait for the target's reply.
|
|
33
|
+
- `intercom_pending`: read queued inbound messages and pending asks.
|
|
34
|
+
- `intercom_reply`: reply to a pending inbound ask.
|
|
35
|
+
|
|
36
|
+
## Workflow
|
|
37
|
+
|
|
38
|
+
1. Call `intercom_status` or `intercom_whoami` to verify this session is
|
|
39
|
+
connected.
|
|
40
|
+
2. Call `intercom_set_summary` with a concise status when other sessions need
|
|
41
|
+
to discover your role.
|
|
42
|
+
3. If this is an orchestrator-owned coworker, call `intercom_team` to get the manager and sibling targets without searching globally. Otherwise use `intercom_list`.
|
|
43
|
+
4. Use `intercom_send` for non-blocking updates and handoffs.
|
|
44
|
+
5. Use `intercom_ask` only when you need the answer before continuing. Assignments, progress/status checkpoints, notifications, and completion reports use `intercom_send`.
|
|
45
|
+
6. Call `intercom_pending` before ending a coordination turn, then answer
|
|
46
|
+
blocking asks with `intercom_reply`.
|
|
47
|
+
|
|
48
|
+
## Patterns
|
|
49
|
+
|
|
50
|
+
Planner delegates without waiting:
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
intercom_send({
|
|
54
|
+
to: "worker",
|
|
55
|
+
message: "Task-3: Add retry logic in src/api/client.ts. Ask if the retry scope is unclear."
|
|
56
|
+
})
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Worker asks and waits:
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
intercom_ask({
|
|
63
|
+
to: "planner",
|
|
64
|
+
message: "Should retry apply only to idempotent endpoints? Proposed: GET/PUT/DELETE, max 3, exponential backoff."
|
|
65
|
+
})
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Reply to an inbound ask:
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
intercom_pending({ mark_read: false })
|
|
72
|
+
intercom_reply({ message: "Use GET/PUT/DELETE only, max 3 retries." })
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
When multiple asks are pending, select the sender with `to`. If that sender has multiple unresolved asks, select `oldest` or `latest` without using protocol IDs:
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
intercom_reply({
|
|
79
|
+
to: "planner",
|
|
80
|
+
which: "oldest",
|
|
81
|
+
message: "Proceed, but preserve the public error shape."
|
|
82
|
+
})
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Keep at most one unresolved `intercom_ask` to the same recipient. Use `intercom_send` for non-blocking follow-ups and every progress/status request.
|
|
86
|
+
|
|
87
|
+
Wake a bridge-managed worker:
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
intercom_ask({
|
|
91
|
+
to: "codex-worker",
|
|
92
|
+
message: "Please inspect the failing test and reply with the most likely cause."
|
|
93
|
+
})
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Boundaries
|
|
97
|
+
|
|
98
|
+
- Do not assume push delivery into Codex. Check `intercom_pending`.
|
|
99
|
+
- Do not assume a normal MCP session can wake itself from idle. Use the
|
|
100
|
+
app-server bridge for wake-on-message virtual workers.
|
|
101
|
+
- Do not use `intercom_ask` for passive polling of files, ports, or process
|
|
102
|
+
completion. Use normal shell checks for those.
|
|
103
|
+
- Keep messages concise and include file paths, command output summaries, and
|
|
104
|
+
decision options when useful.
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"allowImportingTsExtensions": true,
|
|
4
|
+
"esModuleInterop": true,
|
|
5
|
+
"forceConsistentCasingInFileNames": true,
|
|
6
|
+
"module": "NodeNext",
|
|
7
|
+
"moduleResolution": "NodeNext",
|
|
8
|
+
"noEmit": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"strict": false,
|
|
11
|
+
"target": "ES2022",
|
|
12
|
+
"types": ["node"]
|
|
13
|
+
},
|
|
14
|
+
"include": ["*.ts", "broker/**/*.ts", "codex/**/*.ts", "protocol-v4/**/*.ts", "provider/**/*.ts"],
|
|
15
|
+
"exclude": ["node_modules", "**/*.test.ts"]
|
|
16
|
+
}
|
package/types.ts
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
BossControlEnvelope,
|
|
3
|
+
BossParticipantBinding,
|
|
4
|
+
BossParticipantCredentialEnvelope,
|
|
5
|
+
BossRunFeatureContract,
|
|
6
|
+
BrokerCapabilityAdvertisement,
|
|
7
|
+
ParticipantState,
|
|
8
|
+
WorkerIdentityV2,
|
|
9
|
+
} from "@ctliz/agent-intercom-core/boss";
|
|
10
|
+
|
|
11
|
+
export interface BossParticipantRegistrationMetadata {
|
|
12
|
+
featureContract: BossRunFeatureContract;
|
|
13
|
+
credential: BossParticipantCredentialEnvelope;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Broker-owned metadata. Clients may request Boss registration but never assert a binding. */
|
|
17
|
+
export interface BossParticipantBindingMetadata {
|
|
18
|
+
featureContract: BossRunFeatureContract;
|
|
19
|
+
binding: BossParticipantBinding;
|
|
20
|
+
brokerIdentityVerified: true;
|
|
21
|
+
assignedParticipantIds?: string[];
|
|
22
|
+
requestingPrincipalId?: string;
|
|
23
|
+
/** Required by Boss roster projection before a session is discoverable. */
|
|
24
|
+
workerIdentity?: WorkerIdentityV2;
|
|
25
|
+
participantState?: ParticipantState;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface SessionInfo {
|
|
29
|
+
id: string;
|
|
30
|
+
name?: string;
|
|
31
|
+
cwd: string;
|
|
32
|
+
model: string;
|
|
33
|
+
pid: number;
|
|
34
|
+
startedAt: number;
|
|
35
|
+
lastActivity: number;
|
|
36
|
+
status?: string;
|
|
37
|
+
peerUid?: number;
|
|
38
|
+
trustedLocal?: boolean;
|
|
39
|
+
origin?: "local" | "remote";
|
|
40
|
+
remoteHostId?: string;
|
|
41
|
+
parentSessionId?: string;
|
|
42
|
+
rootSessionId?: string;
|
|
43
|
+
generation?: number;
|
|
44
|
+
canDelegate?: boolean;
|
|
45
|
+
depth?: number;
|
|
46
|
+
maxDepth?: number;
|
|
47
|
+
maxChildren?: number;
|
|
48
|
+
boss?: BossParticipantBindingMetadata;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface Message {
|
|
52
|
+
id: string;
|
|
53
|
+
timestamp: number;
|
|
54
|
+
replyTo?: string;
|
|
55
|
+
expectsReply?: boolean;
|
|
56
|
+
content: {
|
|
57
|
+
text: string;
|
|
58
|
+
attachments?: Attachment[];
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface Attachment {
|
|
63
|
+
type: "file" | "snippet" | "context";
|
|
64
|
+
name: string;
|
|
65
|
+
content: string;
|
|
66
|
+
language?: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
type SessionRegistrationBase = Omit<
|
|
70
|
+
SessionInfo,
|
|
71
|
+
"id" | "peerUid" | "trustedLocal" | "origin" | "remoteHostId" | "parentSessionId" | "rootSessionId" | "generation" | "canDelegate" | "depth" | "maxDepth" | "maxChildren" | "boss"
|
|
72
|
+
> & {
|
|
73
|
+
/** Ephemeral identity shared only by reconnects from one live runtime. */
|
|
74
|
+
runtimeInstanceId?: string;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export type OrdinarySessionRegistration = SessionRegistrationBase & {
|
|
78
|
+
boss?: never;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export type BossSessionRegistration = SessionRegistrationBase & {
|
|
82
|
+
/** Optional untrusted request; only a protected, attested broker may return a binding. */
|
|
83
|
+
boss: BossParticipantRegistrationMetadata;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export type SessionRegistration = OrdinarySessionRegistration | BossSessionRegistration;
|
|
87
|
+
|
|
88
|
+
export interface RemoteEnrollmentAccess {
|
|
89
|
+
enrollmentToken: string;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface RemoteSessionAccess {
|
|
93
|
+
sessionCredential: string;
|
|
94
|
+
sessionId: string;
|
|
95
|
+
generation: number;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export type RemoteRegistrationAccess = RemoteEnrollmentAccess | RemoteSessionAccess;
|
|
99
|
+
|
|
100
|
+
export interface RemoteAccessMetadata {
|
|
101
|
+
origin: "remote";
|
|
102
|
+
remoteHostId: string;
|
|
103
|
+
parentSessionId: string;
|
|
104
|
+
rootSessionId: string;
|
|
105
|
+
generation: number;
|
|
106
|
+
canDelegate: boolean;
|
|
107
|
+
depth: number;
|
|
108
|
+
maxDepth: number;
|
|
109
|
+
maxChildren: number;
|
|
110
|
+
sessionCredential?: string;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export interface RemotePrincipalSummary {
|
|
114
|
+
id: string;
|
|
115
|
+
name: string;
|
|
116
|
+
parentSessionId: string;
|
|
117
|
+
rootSessionId: string;
|
|
118
|
+
remoteHostId: string;
|
|
119
|
+
generation: number;
|
|
120
|
+
policy: "remote-tree";
|
|
121
|
+
canDelegate: boolean;
|
|
122
|
+
depth: number;
|
|
123
|
+
maxDepth: number;
|
|
124
|
+
maxChildren: number;
|
|
125
|
+
state: "active" | "revoked";
|
|
126
|
+
expiresAt: number;
|
|
127
|
+
createdAt: number;
|
|
128
|
+
updatedAt: number;
|
|
129
|
+
connected: boolean;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export interface RemoteAccessContract {
|
|
133
|
+
feature: "remote-access-v1";
|
|
134
|
+
policySemanticsVersion: number;
|
|
135
|
+
policySemanticsHash: string;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export type DeliveryFailureCode =
|
|
139
|
+
| "INVALID_MESSAGE"
|
|
140
|
+
| "SESSION_NOT_FOUND"
|
|
141
|
+
| "AMBIGUOUS_TARGET"
|
|
142
|
+
| "SENDER_NOT_FOUND"
|
|
143
|
+
| "INVALID_REPLY_TARGET"
|
|
144
|
+
| "MUTUAL_ASK"
|
|
145
|
+
| "ASK_ALREADY_PENDING"
|
|
146
|
+
| "DUPLICATE_MESSAGE_ID"
|
|
147
|
+
| "CONFLICTING_MESSAGE_ID"
|
|
148
|
+
| "TOO_MANY_PENDING_DELIVERIES"
|
|
149
|
+
| "TOO_MANY_PENDING_ASKS"
|
|
150
|
+
| "RECIPIENT_DISCONNECTED"
|
|
151
|
+
| "SENDER_DISCONNECTED"
|
|
152
|
+
| "DELIVERY_TIMEOUT";
|
|
153
|
+
|
|
154
|
+
export type BossControlFailureCode =
|
|
155
|
+
| "INVALID_CONTROL"
|
|
156
|
+
| "IDEMPOTENCY_CONFLICT"
|
|
157
|
+
| "SESSION_NOT_FOUND"
|
|
158
|
+
| "POLICY_DENIED"
|
|
159
|
+
| "RECIPIENT_DISCONNECTED"
|
|
160
|
+
| "DELIVERY_TIMEOUT";
|
|
161
|
+
|
|
162
|
+
export type BrokerErrorCode =
|
|
163
|
+
| "PROTOCOL_MISMATCH"
|
|
164
|
+
| "INVALID_REQUEST"
|
|
165
|
+
| "SESSION_ID_IN_USE"
|
|
166
|
+
| "ACCESS_DENIED"
|
|
167
|
+
| "REMOTE_ACCESS_INCOMPATIBLE"
|
|
168
|
+
| "BOSS_FEATURE_UNAVAILABLE"
|
|
169
|
+
| "BOSS_CONTRACT_MISMATCH"
|
|
170
|
+
| "RATE_LIMITED"
|
|
171
|
+
| "TOO_MANY_SESSIONS";
|
|
172
|
+
|
|
173
|
+
export type AskCancellationReason =
|
|
174
|
+
| "cancelled"
|
|
175
|
+
| "expired"
|
|
176
|
+
| "delivery_failed"
|
|
177
|
+
| "session_disconnected"
|
|
178
|
+
| "authorization_revoked";
|
|
179
|
+
|
|
180
|
+
export type ClientMessage =
|
|
181
|
+
| { type: "health"; requestId: string; stateId?: string }
|
|
182
|
+
| { type: "register"; protocol: string; version: number; session: OrdinarySessionRegistration; sessionId?: string; stateId?: string; access?: RemoteRegistrationAccess; scopeId?: string }
|
|
183
|
+
| { type: "register"; registrationKind: "boss"; protocol: string; version: number; session: BossSessionRegistration; sessionId?: string; stateId?: string; access?: never; scopeId?: string }
|
|
184
|
+
| { type: "access_control"; requestId: string; adminToken: string; action: "issue_enrollment"; enrollment: { name: string; parentSessionId: string; rootSessionId: string; remoteHostId: string; ttlMs?: number; expiresAt?: number; canDelegate?: boolean; maxDepth?: number; maxChildren?: number } }
|
|
185
|
+
| { type: "access_control"; requestId: string; adminToken: string; action: "revoke_subtree"; principalId: string }
|
|
186
|
+
| { type: "access_control"; requestId: string; adminToken: string; action: "inspect_tree"; principalId: string }
|
|
187
|
+
| { type: "access_control"; requestId: string; adminToken: string; action: "adopt_subtree"; principalId: string; newParentSessionId: string }
|
|
188
|
+
| { type: "access_control"; requestId: string; access: RemoteSessionAccess; action: "issue_child_enrollment"; enrollment: { name: string; ttlMs?: number; expiresAt?: number; canDelegate?: boolean; maxDepth?: number; maxChildren?: number } }
|
|
189
|
+
| { type: "access_control"; requestId: string; access: RemoteSessionAccess; action: "inspect_tree"; principalId?: string }
|
|
190
|
+
| { type: "unregister"; preserveAsks?: boolean }
|
|
191
|
+
| { type: "list"; requestId: string }
|
|
192
|
+
| { type: "send"; to: string; message: Message }
|
|
193
|
+
| { type: "boss_control"; requestId: string; to: string; envelope: BossControlEnvelope }
|
|
194
|
+
| { type: "boss_control_received"; deliveryId: string; messageId: string; idempotencyKey: string }
|
|
195
|
+
| { type: "message_received"; deliveryId: string }
|
|
196
|
+
| { type: "message_rejected"; deliveryId: string; code: "CONFLICTING_MESSAGE_ID"; reason: string }
|
|
197
|
+
| { type: "defer_ask"; requestId: string; messageId: string }
|
|
198
|
+
| { type: "cancel_ask"; requestId: string; messageId: string }
|
|
199
|
+
| { type: "presence"; name?: string; status?: string; model?: string };
|
|
200
|
+
|
|
201
|
+
export type BrokerMessage =
|
|
202
|
+
| { type: "health_ok"; requestId: string; protocol: string; version: number; endpoint: "local" | "remote"; remoteAccess?: RemoteAccessContract; capabilities?: BrokerCapabilityAdvertisement }
|
|
203
|
+
| { type: "registered"; sessionId: string; protocol: string; version: number; remoteAccess?: RemoteAccessContract; access?: RemoteAccessMetadata }
|
|
204
|
+
| { type: "registered"; registrationKind: "boss"; sessionId: string; protocol: string; version: number; capabilities: BrokerCapabilityAdvertisement; boss: BossParticipantBindingMetadata; remoteAccess?: never; access?: never }
|
|
205
|
+
| { type: "access_control_result"; requestId: string; action: "issue_enrollment"; enrollmentToken: string; expiresAt: number }
|
|
206
|
+
| { type: "access_control_result"; requestId: string; action: "revoke_subtree"; changedPrincipalIds: string[] }
|
|
207
|
+
| { type: "access_control_result"; requestId: string; action: "inspect_tree"; principals: RemotePrincipalSummary[] }
|
|
208
|
+
| { type: "access_control_result"; requestId: string; action: "adopt_subtree"; principals: RemotePrincipalSummary[] }
|
|
209
|
+
| { type: "access_control_result"; requestId: string; action: "issue_child_enrollment"; enrollmentToken: string; expiresAt: number; parentSessionId: string }
|
|
210
|
+
| { type: "sessions"; requestId: string; sessions: SessionInfo[] }
|
|
211
|
+
| { type: "message"; deliveryId: string; from: SessionInfo; message: Message }
|
|
212
|
+
| { type: "boss_control"; deliveryId: string; from: SessionInfo; envelope: BossControlEnvelope }
|
|
213
|
+
| { type: "boss_control_ack"; requestId: string; messageId: string; idempotencyKey: string; status: "accepted"; deliveryId: string }
|
|
214
|
+
| { type: "boss_control_result"; requestId: string; messageId: string; idempotencyKey: string; status: "delivered"; delivered: true; deliveryId: string }
|
|
215
|
+
| { type: "boss_control_result"; requestId: string; messageId: string; idempotencyKey: string; status: "rejected"; delivered: false; deliveryId?: string; code: BossControlFailureCode; reason: string }
|
|
216
|
+
| { type: "presence_update"; session: SessionInfo }
|
|
217
|
+
| { type: "session_joined"; session: SessionInfo }
|
|
218
|
+
| { type: "session_left"; sessionId: string }
|
|
219
|
+
| { type: "error"; code: BrokerErrorCode; error: string }
|
|
220
|
+
| { type: "delivery_accepted"; messageId: string; deliveryId: string }
|
|
221
|
+
| { type: "delivered"; messageId: string; deliveryId: string }
|
|
222
|
+
| { type: "delivery_failed"; messageId: string; accepted: boolean; code: DeliveryFailureCode; reason: string }
|
|
223
|
+
| { type: "ask_deferred"; messageId: string; fromSessionId: string }
|
|
224
|
+
| { type: "ask_cancelled"; messageId: string; fromSessionId: string; reason: AskCancellationReason }
|
|
225
|
+
| { type: "ask_control_result"; requestId: string; action: "defer" | "cancel"; messageId: string; applied: boolean };
|