copilot-hub 0.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.
Files changed (128) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +215 -0
  3. package/apps/agent-engine/.env.example +41 -0
  4. package/apps/agent-engine/LICENSE +21 -0
  5. package/apps/agent-engine/README.md +57 -0
  6. package/apps/agent-engine/bot-registry.example.json +28 -0
  7. package/apps/agent-engine/capabilities/example/index.js +3 -0
  8. package/apps/agent-engine/capabilities/example/manifest.json +14 -0
  9. package/apps/agent-engine/dist/agent-worker.js +241 -0
  10. package/apps/agent-engine/dist/config.js +225 -0
  11. package/apps/agent-engine/dist/index.js +352 -0
  12. package/apps/agent-engine/dist/test/project-fingerprint.test.js +40 -0
  13. package/apps/agent-engine/dist/test/thread-id.test.js +12 -0
  14. package/apps/agent-engine/package.json +28 -0
  15. package/apps/control-plane/.env.example +25 -0
  16. package/apps/control-plane/README.md +35 -0
  17. package/apps/control-plane/bot-registry.example.json +40 -0
  18. package/apps/control-plane/capabilities/example/index.js +3 -0
  19. package/apps/control-plane/capabilities/example/manifest.json +14 -0
  20. package/apps/control-plane/dist/agent-worker.js +243 -0
  21. package/apps/control-plane/dist/channels/channel-factory.js +21 -0
  22. package/apps/control-plane/dist/channels/hub-ops-commands.js +752 -0
  23. package/apps/control-plane/dist/channels/telegram-channel.js +743 -0
  24. package/apps/control-plane/dist/channels/whatsapp-channel.js +35 -0
  25. package/apps/control-plane/dist/config.js +230 -0
  26. package/apps/control-plane/dist/copilot-hub.js +138 -0
  27. package/apps/control-plane/dist/index.js +349 -0
  28. package/apps/control-plane/dist/kernel/admin-contract.js +51 -0
  29. package/apps/control-plane/dist/test/project-fingerprint.test.js +40 -0
  30. package/apps/control-plane/dist/test/thread-id.test.js +12 -0
  31. package/apps/control-plane/package.json +27 -0
  32. package/package.json +89 -0
  33. package/packages/contracts/README.md +10 -0
  34. package/packages/contracts/dist/control-plane.d.ts +24 -0
  35. package/packages/contracts/dist/control-plane.js +37 -0
  36. package/packages/contracts/dist/control-plane.js.map +1 -0
  37. package/packages/contracts/dist/index.d.ts +1 -0
  38. package/packages/contracts/dist/index.js +2 -0
  39. package/packages/contracts/dist/index.js.map +1 -0
  40. package/packages/contracts/package.json +27 -0
  41. package/packages/core/README.md +33 -0
  42. package/packages/core/dist/agent-supervisor.d.ts +39 -0
  43. package/packages/core/dist/agent-supervisor.js +552 -0
  44. package/packages/core/dist/agent-supervisor.js.map +1 -0
  45. package/packages/core/dist/bot-manager.d.ts +66 -0
  46. package/packages/core/dist/bot-manager.js +333 -0
  47. package/packages/core/dist/bot-manager.js.map +1 -0
  48. package/packages/core/dist/bot-registry.d.ts +60 -0
  49. package/packages/core/dist/bot-registry.js +381 -0
  50. package/packages/core/dist/bot-registry.js.map +1 -0
  51. package/packages/core/dist/bot-runtime.d.ts +135 -0
  52. package/packages/core/dist/bot-runtime.js +349 -0
  53. package/packages/core/dist/bot-runtime.js.map +1 -0
  54. package/packages/core/dist/bridge-service.d.ts +39 -0
  55. package/packages/core/dist/bridge-service.js +272 -0
  56. package/packages/core/dist/bridge-service.js.map +1 -0
  57. package/packages/core/dist/capability-manager.d.ts +18 -0
  58. package/packages/core/dist/capability-manager.js +335 -0
  59. package/packages/core/dist/capability-manager.js.map +1 -0
  60. package/packages/core/dist/capability-scaffold.d.ts +26 -0
  61. package/packages/core/dist/capability-scaffold.js +118 -0
  62. package/packages/core/dist/capability-scaffold.js.map +1 -0
  63. package/packages/core/dist/channel-factory.d.ts +6 -0
  64. package/packages/core/dist/channel-factory.js +22 -0
  65. package/packages/core/dist/channel-factory.js.map +1 -0
  66. package/packages/core/dist/codex-app-client.d.ts +56 -0
  67. package/packages/core/dist/codex-app-client.js +762 -0
  68. package/packages/core/dist/codex-app-client.js.map +1 -0
  69. package/packages/core/dist/codex-provider.d.ts +31 -0
  70. package/packages/core/dist/codex-provider.js +64 -0
  71. package/packages/core/dist/codex-provider.js.map +1 -0
  72. package/packages/core/dist/control-permission.d.ts +19 -0
  73. package/packages/core/dist/control-permission.js +106 -0
  74. package/packages/core/dist/control-permission.js.map +1 -0
  75. package/packages/core/dist/control-plane-actions.d.ts +1 -0
  76. package/packages/core/dist/control-plane-actions.js +2 -0
  77. package/packages/core/dist/control-plane-actions.js.map +1 -0
  78. package/packages/core/dist/example-capability.d.ts +17 -0
  79. package/packages/core/dist/example-capability.js +22 -0
  80. package/packages/core/dist/example-capability.js.map +1 -0
  81. package/packages/core/dist/extension-contract.d.ts +22 -0
  82. package/packages/core/dist/extension-contract.js +28 -0
  83. package/packages/core/dist/extension-contract.js.map +1 -0
  84. package/packages/core/dist/index.d.ts +26 -0
  85. package/packages/core/dist/index.js +27 -0
  86. package/packages/core/dist/index.js.map +1 -0
  87. package/packages/core/dist/instance-lock.d.ts +9 -0
  88. package/packages/core/dist/instance-lock.js +74 -0
  89. package/packages/core/dist/instance-lock.js.map +1 -0
  90. package/packages/core/dist/kernel-control-plane.d.ts +16 -0
  91. package/packages/core/dist/kernel-control-plane.js +500 -0
  92. package/packages/core/dist/kernel-control-plane.js.map +1 -0
  93. package/packages/core/dist/kernel-version.d.ts +1 -0
  94. package/packages/core/dist/kernel-version.js +2 -0
  95. package/packages/core/dist/kernel-version.js.map +1 -0
  96. package/packages/core/dist/project-fingerprint.d.ts +11 -0
  97. package/packages/core/dist/project-fingerprint.js +33 -0
  98. package/packages/core/dist/project-fingerprint.js.map +1 -0
  99. package/packages/core/dist/provider-factory.d.ts +7 -0
  100. package/packages/core/dist/provider-factory.js +21 -0
  101. package/packages/core/dist/provider-factory.js.map +1 -0
  102. package/packages/core/dist/secret-store.d.ts +18 -0
  103. package/packages/core/dist/secret-store.js +110 -0
  104. package/packages/core/dist/secret-store.js.map +1 -0
  105. package/packages/core/dist/state-store.d.ts +50 -0
  106. package/packages/core/dist/state-store.js +324 -0
  107. package/packages/core/dist/state-store.js.map +1 -0
  108. package/packages/core/dist/telegram-channel.d.ts +27 -0
  109. package/packages/core/dist/telegram-channel.js +951 -0
  110. package/packages/core/dist/telegram-channel.js.map +1 -0
  111. package/packages/core/dist/thread-id.d.ts +1 -0
  112. package/packages/core/dist/thread-id.js +12 -0
  113. package/packages/core/dist/thread-id.js.map +1 -0
  114. package/packages/core/dist/whatsapp-channel.d.ts +26 -0
  115. package/packages/core/dist/whatsapp-channel.js +36 -0
  116. package/packages/core/dist/whatsapp-channel.js.map +1 -0
  117. package/packages/core/dist/workspace-paths.d.ts +5 -0
  118. package/packages/core/dist/workspace-paths.js +77 -0
  119. package/packages/core/dist/workspace-paths.js.map +1 -0
  120. package/packages/core/dist/workspace-policy.d.ts +30 -0
  121. package/packages/core/dist/workspace-policy.js +104 -0
  122. package/packages/core/dist/workspace-policy.js.map +1 -0
  123. package/packages/core/package.json +126 -0
  124. package/scripts/cli.mjs +537 -0
  125. package/scripts/configure.mjs +254 -0
  126. package/scripts/ensure-shared-build.mjs +96 -0
  127. package/scripts/run-node-tests.mjs +52 -0
  128. package/scripts/supervisor.mjs +332 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 openminedev
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,215 @@
1
+ # Copilot Hub
2
+
3
+ Copilot Hub is a 2-plane monorepo for building and operating Telegram AI agents.
4
+
5
+ ## Planes
6
+
7
+ - `apps/control-plane`: single Telegram hub chat for operations commands and LLM development tasks.
8
+ - `apps/agent-engine`: execution plane (workers, channels, sessions, capabilities).
9
+
10
+ Shared packages:
11
+
12
+ - `packages/contracts`
13
+ - `packages/core`
14
+ - `packages/capabilities`
15
+
16
+ ## Hub chat model
17
+
18
+ The same Telegram chat handles both operations and development:
19
+
20
+ - commands: `/help`, `/health`, `/bots`, `/create_agent`, `/cancel`
21
+ - normal message: handled by the LLM assistant
22
+
23
+ ## Workspace isolation
24
+
25
+ Each runtime agent has its own dedicated `workspaceRoot`.
26
+
27
+ ```mermaid
28
+ flowchart LR
29
+ CA["Chat A"] --> A["Agent A"] --> WA["~/Desktop/copilot_workspaces/Agent_A"]
30
+ CB["Chat B"] --> B["Agent B"] --> WB["~/Desktop/copilot_workspaces/Agent_B"]
31
+ CC["Chat C"] --> C["Agent C"] --> WC["~/Desktop/copilot_workspaces/Agent_C"]
32
+ ```
33
+
34
+ Why this matters:
35
+
36
+ - multiple chats can run at the same time without mixing files
37
+ - multiple projects can run in parallel on the same machine
38
+ - one agent change stays inside that agent workspace
39
+
40
+ ## Control-plane role
41
+
42
+ - `control chat` is a simple maintenance chat: fix issues and add new capabilities.
43
+ - `runtime chats` are user-facing chats: execute tasks and deliver results.
44
+ - users stay in runtime chats for normal work and use control chat only when an agent needs a fix or upgrade.
45
+
46
+ ## Example flow
47
+
48
+ Scenario: user asks a runtime chat (Agent A) to create a website from an image.
49
+
50
+ ```mermaid
51
+ flowchart TD
52
+ U1["User -> Runtime chat (Agent A): Create a website from this image"] --> C{"Can Agent A read images?"}
53
+ C -- Yes --> O1["Agent A builds the website"]
54
+ C -- No --> M["Agent A says: I cannot read images yet"]
55
+
56
+ M --> U3["User -> Control chat (control-plane): Fix this for Agent A"]
57
+ U3 --> I["Control chat adds image_reader to Agent A workspace"]
58
+ I --> T{"Works correctly?"}
59
+ T -- No --> U3
60
+ T -- Yes --> R["Agent A reloads capabilities"]
61
+
62
+ R --> U4["User -> Runtime chat (Agent A): Continue the website task"]
63
+ U4 --> O1
64
+ ```
65
+
66
+ ## Install from npm
67
+
68
+ ```bash
69
+ npm install -g copilot-hub@latest
70
+ ```
71
+
72
+ Then run:
73
+
74
+ ```bash
75
+ copilot-hub configure
76
+ copilot-hub start
77
+ ```
78
+
79
+ ## Quick start from source
80
+
81
+ 1. Install dependencies:
82
+
83
+ ```bash
84
+ npm install
85
+ ```
86
+
87
+ 2. Configure the required hub token:
88
+
89
+ ```bash
90
+ npm run configure
91
+ ```
92
+
93
+ 3. Start services:
94
+
95
+ ```bash
96
+ npm run start
97
+ ```
98
+
99
+ `start` checks required tokens and prompts only if values are missing.
100
+
101
+ ## Telegram setup
102
+
103
+ ### 1) Create the control-plane (hub) bot token
104
+
105
+ 1. Open Telegram and search for `@BotFather`.
106
+ 2. Send `/start`.
107
+ 3. Send `/newbot`.
108
+ 4. Enter the bot display name you want.
109
+ 5. Enter a unique username ending with `bot` (example: `my_copilot_hub_bot`).
110
+ 6. BotFather returns a token like `123456789:AA...`.
111
+
112
+ Now run:
113
+
114
+ ```bash
115
+ npm run configure
116
+ ```
117
+
118
+ When prompted, paste this token.
119
+
120
+ ### 2) Start Copilot Hub
121
+
122
+ ```bash
123
+ npm run start
124
+ ```
125
+
126
+ Then open your hub bot in Telegram and send `/start`.
127
+
128
+ Hub commands:
129
+
130
+ - `/help`
131
+ - `/health`
132
+ - `/bots`
133
+ - `/create_agent`
134
+ - `/cancel`
135
+
136
+ ### 3) Create runtime agent bot(s)
137
+
138
+ You need one Telegram bot token per runtime agent.
139
+
140
+ 1. Go back to `@BotFather`.
141
+ 2. Run `/newbot` again.
142
+ 3. Create a new bot for the runtime agent.
143
+ 4. Copy the new token.
144
+ 5. In the hub chat, run `/create_agent` and follow the wizard:
145
+ - Step 1: send the runtime agent token
146
+ - Step 2: send agent id (or `default`)
147
+ - Step 3: reply `YES`
148
+
149
+ After creation, use `/bots` in the hub chat to manage policy, reset context, or delete an agent.
150
+ A streamlined default profile is applied, and actions start from that agent workspace folder.
151
+
152
+ ### 4) Token safety
153
+
154
+ - Never commit real bot tokens.
155
+ - If a token is leaked, regenerate it in `@BotFather` using `/revoke`.
156
+ - Keep local runtime files (`data/`, `logs/`) private.
157
+
158
+ ## Startup troubleshooting
159
+
160
+ - If `npm run start` fails, first read the error and follow the suggested action.
161
+ - `npm run start` now auto-detects Codex from VS Code (Windows) and can install Codex CLI automatically if missing.
162
+ - For Codex login issues, run `codex login` (or the configured `CODEX_BIN`) and retry `npm run start`.
163
+ - If auto-install is skipped or unavailable, install Codex CLI with `npm install -g @openai/codex` or set `CODEX_BIN` in `.env`.
164
+ - If you are still stuck, ask your favorite LLM with the exact error output.
165
+
166
+ ## Commands
167
+
168
+ ```bash
169
+ npm run start
170
+ npm run stop
171
+ npm run restart
172
+ npm run status
173
+ npm run logs
174
+ npm run configure
175
+ npm run test
176
+ npm run lint
177
+ npm run format:check
178
+ npm run check:apps
179
+ ```
180
+
181
+ ## npm release (CI)
182
+
183
+ Publishing is automated from GitHub Actions on tags (`v*`).
184
+
185
+ Release flow:
186
+
187
+ ```bash
188
+ npm version patch
189
+ git push origin main --follow-tags
190
+ ```
191
+
192
+ The release workflow validates build/test/lint/format, checks that the tag version matches `package.json`, then publishes to npm.
193
+
194
+ Authentication options:
195
+
196
+ - Recommended: npm Trusted Publishing (OIDC/provenance)
197
+ - Fallback: set `NPM_TOKEN` in GitHub repository secrets
198
+
199
+ ## Workspace policy
200
+
201
+ - Default workspace root: `~/Desktop/copilot_workspaces` when not explicitly set.
202
+ - `WORKSPACE_STRICT_MODE=true` enforces allowed roots.
203
+ - `WORKSPACE_ALLOWED_ROOTS` adds extra allowed roots.
204
+ - Workspaces inside kernel directories are rejected.
205
+
206
+ ## Runtime files
207
+
208
+ - PIDs: `.copilot-hub/pids/`
209
+ - Logs: `logs/`
210
+
211
+ ## Security
212
+
213
+ - Never commit real tokens.
214
+ - Keep `.env` and runtime data local.
215
+ - Rotate leaked tokens immediately.
@@ -0,0 +1,41 @@
1
+ # Runtime state files
2
+ BOT_DATA_DIR=./data
3
+ BOT_REGISTRY_FILE=./data/bot-registry.json
4
+ SECRET_STORE_FILE=./data/secrets.json
5
+
6
+
7
+ # Worker defaults
8
+ # If empty, runtime uses Desktop/copilot_workspaces by default
9
+ # (Windows: %USERPROFILE%/Desktop/copilot_workspaces)
10
+ DEFAULT_WORKSPACE_ROOT=
11
+ PROJECTS_BASE_DIR=
12
+ WORKSPACE_STRICT_MODE=true
13
+ WORKSPACE_ALLOWED_ROOTS=
14
+ DEFAULT_PROVIDER_KIND=codex
15
+ CODEX_HOME_DIR=
16
+ CODEX_SANDBOX=danger-full-access
17
+ CODEX_APPROVAL_POLICY=never
18
+ TURN_ACTIVITY_TIMEOUT_MS=3600000
19
+ MAX_THREAD_MESSAGES=200
20
+
21
+ AGENT_HEARTBEAT_ENABLED=true
22
+ AGENT_HEARTBEAT_INTERVAL_MS=5000
23
+ AGENT_HEARTBEAT_TIMEOUT_MS=4000
24
+
25
+ THREAD_MODE=single
26
+ SHARED_THREAD_ID=shared-main
27
+ TELEGRAM_ALLOWED_CHAT_IDS=
28
+
29
+ INSTANCE_LOCK_ENABLED=true
30
+ INSTANCE_LOCK_FILE=./data/runtime.lock
31
+
32
+ WEB_HOST=127.0.0.1
33
+ WEB_PORT=8787
34
+ WEB_PUBLIC_BASE_URL=http://localhost:8787
35
+ WEB_PORT_AUTO_INCREMENT=true
36
+ WEB_PORT_SEARCH_MAX=30
37
+
38
+ # Optional worker token environment variables used in bot-registry.json
39
+ TELEGRAM_TOKEN_AGENT_1=
40
+
41
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 amine
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,57 @@
1
+ # Agent Engine
2
+
3
+ `agent-engine` is the execution plane for worker agents, Telegram channels, capabilities, projects, and runtime policy.
4
+
5
+ In Copilot Hub:
6
+ - `apps/control-plane`: single Telegram hub chat (operations + LLM development)
7
+ - `apps/agent-engine`: runtime execution plane
8
+
9
+ ## Quick start
10
+
11
+ ```bash
12
+ npm install
13
+ # Windows
14
+ copy .env.example .env
15
+ # macOS/Linux
16
+ cp .env.example .env
17
+ npm run setup
18
+ npm run start
19
+ ```
20
+
21
+ ## Operator entry point
22
+
23
+ Use `apps/control-plane` as the main operator chat.
24
+
25
+ ## Workspace policy
26
+
27
+ - If `DEFAULT_WORKSPACE_ROOT` is empty, default root is `~/Desktop/copilot_workspaces`.
28
+ - `WORKSPACE_STRICT_MODE=true` enforces allowed roots.
29
+ - `WORKSPACE_ALLOWED_ROOTS` lets you append extra allowed roots.
30
+ - Agent workspaces must stay outside the kernel directory.
31
+
32
+ ## Runtime API
33
+
34
+ Core:
35
+ - `GET /api/health`
36
+ - `GET /api/bots`
37
+ - `POST /api/bots/create`
38
+ - `POST /api/bots/:botId/delete`
39
+ - `POST /api/bots/:botId/policy`
40
+ - `POST /api/bots/:botId/reset`
41
+
42
+ Projects:
43
+ - `GET /api/projects`
44
+ - `POST /api/projects/create`
45
+ - `POST /api/bots/:botId/project`
46
+
47
+ Capabilities and approvals:
48
+ - `GET /api/bots/:botId/capabilities`
49
+ - `POST /api/bots/:botId/capabilities/reload`
50
+ - `POST /api/bots/:botId/capabilities/scaffold`
51
+ - `GET /api/bots/:botId/approvals`
52
+ - `POST /api/bots/:botId/approvals/:approvalId`
53
+
54
+ ## Security
55
+
56
+ - Never commit `.env` or `data/`.
57
+ - Rotate exposed tokens.
@@ -0,0 +1,28 @@
1
+ {
2
+ "version": 3,
3
+ "agents": [
4
+ {
5
+ "id": "Agent_1",
6
+ "name": "Agent 1",
7
+ "enabled": true,
8
+ "autoStart": true,
9
+ "threadMode": "single",
10
+ "sharedThreadId": "shared-Agent_1",
11
+ "provider": {
12
+ "kind": "codex",
13
+ "options": {
14
+ "sandboxMode": "danger-full-access",
15
+ "approvalPolicy": "never"
16
+ }
17
+ },
18
+ "channels": [
19
+ {
20
+ "kind": "telegram",
21
+ "id": "telegram_Agent_1",
22
+ "tokenEnv": "TELEGRAM_TOKEN_AGENT_1"
23
+ }
24
+ ],
25
+ "capabilities": []
26
+ }
27
+ ]
28
+ }
@@ -0,0 +1,3 @@
1
+ import { createExampleCapability } from "@copilot-hub/core/example-capability";
2
+
3
+ export const createCapability = createExampleCapability;
@@ -0,0 +1,14 @@
1
+ {
2
+ "id": "example-capability",
3
+ "name": "Example Capability",
4
+ "version": "1.0.0",
5
+ "entry": "./index.js",
6
+ "minKernelVersion": "1.0.0",
7
+ "timeoutMs": 3000,
8
+ "hooks": [
9
+ "onRuntimeStart",
10
+ "onTurnStart",
11
+ "onTurnResult"
12
+ ],
13
+ "permissions": []
14
+ }
@@ -0,0 +1,241 @@
1
+ // @ts-nocheck
2
+ import { BotRuntime } from "@copilot-hub/core/bot-runtime";
3
+ const rawBotConfig = String(process.env.AGENT_BOT_CONFIG_JSON ?? "").trim();
4
+ const rawProviderDefaults = String(process.env.AGENT_PROVIDER_DEFAULTS_JSON ?? "").trim();
5
+ const turnActivityTimeoutMs = Number.parseInt(String(process.env.AGENT_TURN_ACTIVITY_TIMEOUT_MS ?? "3600000"), 10);
6
+ const maxMessages = Number.parseInt(String(process.env.AGENT_MAX_MESSAGES ?? "200"), 10);
7
+ const initialWebPublicBaseUrl = String(process.env.AGENT_WEB_PUBLIC_BASE_URL ?? "http://127.0.0.1:8787").trim();
8
+ const kernelRequestTimeoutMs = Number.parseInt(String(process.env.AGENT_KERNEL_REQUEST_TIMEOUT_MS ?? "20000"), 10);
9
+ if (!rawBotConfig) {
10
+ throw new Error("AGENT_BOT_CONFIG_JSON is required.");
11
+ }
12
+ const botConfig = JSON.parse(rawBotConfig);
13
+ const providerDefaults = rawProviderDefaults ? JSON.parse(rawProviderDefaults) : {};
14
+ let nextKernelRequestId = 1;
15
+ const pendingKernelRequests = new Map();
16
+ const runtime = new BotRuntime({
17
+ botConfig,
18
+ providerDefaults,
19
+ turnActivityTimeoutMs,
20
+ maxMessages,
21
+ kernelControl: {
22
+ request: (payload) => requestKernelAction(payload),
23
+ },
24
+ });
25
+ runtime.setWebPublicBaseUrl(initialWebPublicBaseUrl);
26
+ process.on("message", (message) => {
27
+ void handleInboundMessage(message);
28
+ });
29
+ process.on("disconnect", () => {
30
+ void gracefulShutdown(0);
31
+ });
32
+ process.on("SIGINT", () => {
33
+ void gracefulShutdown(0);
34
+ });
35
+ process.on("SIGTERM", () => {
36
+ void gracefulShutdown(0);
37
+ });
38
+ process.on("uncaughtException", (error) => {
39
+ console.error(`Worker uncaught exception: ${sanitizeError(error)}`);
40
+ void gracefulShutdown(1);
41
+ });
42
+ process.on("unhandledRejection", (reason) => {
43
+ console.error(`Worker unhandled rejection: ${sanitizeError(reason)}`);
44
+ void gracefulShutdown(1);
45
+ });
46
+ sendEvent("workerReady", {
47
+ runtimeId: runtime.id,
48
+ name: runtime.name,
49
+ });
50
+ async function handleInboundMessage(message) {
51
+ const type = String(message?.type ?? "request").trim();
52
+ if (type === "kernelResponse") {
53
+ handleKernelResponse(message);
54
+ return;
55
+ }
56
+ if (type !== "request") {
57
+ return;
58
+ }
59
+ try {
60
+ await handleWorkerRequest(message);
61
+ }
62
+ catch (error) {
63
+ const requestId = message?.requestId ?? null;
64
+ if (requestId !== null && requestId !== undefined) {
65
+ sendResponse({
66
+ requestId,
67
+ ok: false,
68
+ error: sanitizeError(error),
69
+ });
70
+ return;
71
+ }
72
+ console.error(`Worker inbound message failed: ${sanitizeError(error)}`);
73
+ }
74
+ }
75
+ async function handleWorkerRequest(message) {
76
+ const requestId = message?.requestId;
77
+ const action = String(message?.action ?? "").trim();
78
+ const payload = message?.payload ?? {};
79
+ if (!requestId) {
80
+ throw new Error("requestId is required.");
81
+ }
82
+ if (!action) {
83
+ throw new Error("action is required.");
84
+ }
85
+ let result;
86
+ switch (action) {
87
+ case "getStatus": {
88
+ result = runtime.getStatus();
89
+ break;
90
+ }
91
+ case "startChannels": {
92
+ result = await runtime.startChannels();
93
+ break;
94
+ }
95
+ case "stopChannels": {
96
+ result = await runtime.stopChannels();
97
+ break;
98
+ }
99
+ case "resetWebThread": {
100
+ result = await runtime.resetWebThread();
101
+ break;
102
+ }
103
+ case "listPendingApprovals": {
104
+ const threadId = payload?.threadId ? String(payload.threadId) : undefined;
105
+ result = await runtime.listPendingApprovals(threadId);
106
+ break;
107
+ }
108
+ case "resolvePendingApproval": {
109
+ result = await runtime.resolvePendingApproval({
110
+ threadId: String(payload?.threadId ?? ""),
111
+ approvalId: String(payload?.approvalId ?? ""),
112
+ decision: String(payload?.decision ?? ""),
113
+ });
114
+ break;
115
+ }
116
+ case "reloadCapabilities": {
117
+ const capabilityDefinitions = Array.isArray(payload?.capabilityDefinitions)
118
+ ? payload.capabilityDefinitions
119
+ : null;
120
+ result = await runtime.reloadCapabilities(capabilityDefinitions);
121
+ break;
122
+ }
123
+ case "setProjectRoot": {
124
+ const projectRoot = String(payload?.projectRoot ?? "").trim();
125
+ if (!projectRoot) {
126
+ throw new Error("projectRoot is required.");
127
+ }
128
+ await runtime.setProjectRoot(projectRoot);
129
+ result = runtime.getStatus();
130
+ break;
131
+ }
132
+ case "setWebPublicBaseUrl": {
133
+ const value = String(payload?.webPublicBaseUrl ?? "").trim();
134
+ if (!value) {
135
+ throw new Error("webPublicBaseUrl is required.");
136
+ }
137
+ runtime.setWebPublicBaseUrl(value);
138
+ result = runtime.getStatus();
139
+ break;
140
+ }
141
+ case "shutdown": {
142
+ await runtime.shutdown();
143
+ result = { ok: true };
144
+ sendResponse({ requestId, ok: true, result });
145
+ process.exit(0);
146
+ return;
147
+ }
148
+ default: {
149
+ throw new Error(`Unsupported worker action '${action}'.`);
150
+ }
151
+ }
152
+ sendResponse({ requestId, ok: true, result });
153
+ }
154
+ async function gracefulShutdown(exitCode) {
155
+ try {
156
+ await runtime.shutdown();
157
+ }
158
+ catch {
159
+ // Ignore shutdown errors during process termination.
160
+ }
161
+ for (const pending of pendingKernelRequests.values()) {
162
+ clearTimeout(pending.timer);
163
+ pending.reject(new Error("Worker process is shutting down."));
164
+ }
165
+ pendingKernelRequests.clear();
166
+ process.exit(exitCode);
167
+ }
168
+ function sendResponse(value) {
169
+ if (!process.send) {
170
+ return;
171
+ }
172
+ process.send({
173
+ type: "response",
174
+ ...value,
175
+ });
176
+ }
177
+ function sendEvent(event, payload) {
178
+ if (!process.send) {
179
+ return;
180
+ }
181
+ process.send({
182
+ type: "event",
183
+ event,
184
+ payload,
185
+ });
186
+ }
187
+ function requestKernelAction({ action, payload, context }) {
188
+ if (!process.send) {
189
+ return Promise.reject(new Error("Kernel IPC is unavailable."));
190
+ }
191
+ const requestId = `kreq_${Date.now()}_${nextKernelRequestId++}`;
192
+ const timeoutMs = Number.isFinite(kernelRequestTimeoutMs) && kernelRequestTimeoutMs >= 1000
193
+ ? kernelRequestTimeoutMs
194
+ : 20000;
195
+ return new Promise((resolve, reject) => {
196
+ const timer = setTimeout(() => {
197
+ pendingKernelRequests.delete(requestId);
198
+ reject(new Error(`Kernel request '${String(action ?? "")}' timed out after ${timeoutMs}ms.`));
199
+ }, timeoutMs);
200
+ pendingKernelRequests.set(requestId, {
201
+ resolve,
202
+ reject,
203
+ timer,
204
+ });
205
+ try {
206
+ process.send({
207
+ type: "kernelRequest",
208
+ requestId,
209
+ action,
210
+ payload,
211
+ context,
212
+ });
213
+ }
214
+ catch (error) {
215
+ clearTimeout(timer);
216
+ pendingKernelRequests.delete(requestId);
217
+ reject(error);
218
+ }
219
+ });
220
+ }
221
+ function handleKernelResponse(message) {
222
+ const requestId = String(message?.requestId ?? "").trim();
223
+ if (!requestId) {
224
+ return;
225
+ }
226
+ const pending = pendingKernelRequests.get(requestId);
227
+ if (!pending) {
228
+ return;
229
+ }
230
+ pendingKernelRequests.delete(requestId);
231
+ clearTimeout(pending.timer);
232
+ if (message?.ok) {
233
+ pending.resolve(message.result);
234
+ return;
235
+ }
236
+ pending.reject(new Error(String(message?.error ?? "Unknown kernel response error.")));
237
+ }
238
+ function sanitizeError(error) {
239
+ const raw = error instanceof Error ? error.message : String(error);
240
+ return raw.split(/\r?\n/).slice(0, 12).join("\n");
241
+ }