@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.
Files changed (52) hide show
  1. package/.codex-plugin/plugin.json +39 -0
  2. package/.mcp.json +10 -0
  3. package/COPYRIGHT +6 -0
  4. package/LICENSE +661 -0
  5. package/LICENSE_TRANSITION.md +25 -0
  6. package/README.md +572 -0
  7. package/THIRD_PARTY_NOTICES.md +17 -0
  8. package/assets/logo-generated.png +0 -0
  9. package/assets/logo.svg +22 -0
  10. package/boss-control-outbox.ts +195 -0
  11. package/broker/access-credential.ts +76 -0
  12. package/broker/access-registry.ts +464 -0
  13. package/broker/audit.ts +60 -0
  14. package/broker/authorization.ts +54 -0
  15. package/broker/boss-adapter.ts +531 -0
  16. package/broker/boss-control-ledger.ts +306 -0
  17. package/broker/broker.ts +2327 -0
  18. package/broker/client.ts +1111 -0
  19. package/broker/framing.ts +87 -0
  20. package/broker/ownership.ts +61 -0
  21. package/broker/paths.ts +161 -0
  22. package/broker/spawn.ts +453 -0
  23. package/codex/app-server-client.ts +562 -0
  24. package/codex/boss-client.ts +34 -0
  25. package/codex/bridge-config.ts +282 -0
  26. package/codex/bridge-daemon.ts +913 -0
  27. package/codex/clipboard.ts +142 -0
  28. package/codex/coi.ts +802 -0
  29. package/codex/contact.ts +54 -0
  30. package/codex/mcp-protocol.ts +248 -0
  31. package/codex/runtime.ts +488 -0
  32. package/codex/server.ts +60 -0
  33. package/codex/team.ts +249 -0
  34. package/codex/tui-input.ts +174 -0
  35. package/config.ts +195 -0
  36. package/dist/bridge-daemon.mjs +3781 -0
  37. package/dist/broker.mjs +3012 -0
  38. package/dist/build-info.json +12 -0
  39. package/dist/codex-server.mjs +2890 -0
  40. package/dist/coi.mjs +4686 -0
  41. package/durable-json.ts +25 -0
  42. package/licenses/MIT-pi-intercom.txt +21 -0
  43. package/outbound-outbox.ts +116 -0
  44. package/package.json +89 -0
  45. package/protocol-v4/contract.ts +27 -0
  46. package/provider/protected-service.ts +162 -0
  47. package/provider/provider.mjs +34 -0
  48. package/scripts/build-info.mjs +58 -0
  49. package/scripts/build.mjs +55 -0
  50. package/skills/codex-intercom/SKILL.md +104 -0
  51. package/tsconfig.json +16 -0
  52. 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 };