arisa 5.1.60 → 5.1.64
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 +9 -0
- package/package.json +1 -1
- package/src/core/agent/agent-manager.js +10 -0
- package/src/core/config/config-defaults.js +28 -1
- package/src/core/tasks/task-runner.js +17 -2
- package/src/core/tasks/task-store.js +80 -14
- package/src/core/tools/tool-registry.js +20 -5
- package/src/core/tools/weighted-resource-governor.js +153 -0
- package/src/index.js +20 -0
- package/src/official-tools.lock.json +56 -42
- package/src/runtime/create-app.js +1 -1
- package/src/runtime/create-headless-app.js +2 -2
- package/src/runtime/paths.js +4 -0
- package/src/runtime/service-manager.js +3 -1
- package/src/runtime/service-supervisor.js +98 -0
- package/src/transport/telegram/bot.js +77 -9
- package/src/transport/telegram/chat-queue.js +13 -2
- package/src/transport/telegram/reply-topic-routing.js +111 -0
- package/src/transport/telegram/task-dispatcher.js +26 -3
- package/src/transport/telegram/telegram-session-bridge.js +7 -0
- package/src/transport/telegram/workspace-topic-store.js +228 -0
- package/test/chat-queue.test.js +32 -0
- package/test/model-selection.test.js +9 -1
- package/test/official-tool-dependencies.test.js +1 -1
- package/test/paths.test.js +8 -0
- package/test/service-manager.test.js +48 -0
- package/test/task-store.test.js +56 -4
- package/test/telegram-reply-topic-routing.test.js +94 -0
- package/test/telegram-task-dispatcher.test.js +51 -0
- package/test/telegram-workspace-topic-store.test.js +124 -0
- package/test/tool-registry-run.test.js +41 -0
- package/test/weighted-resource-governor.test.js +95 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 1,
|
|
3
3
|
"repository": "https://github.com/clasen/Arisa.git",
|
|
4
|
-
"commit": "
|
|
4
|
+
"commit": "410d18e354374b43a225c31be574deb4b8f69aba",
|
|
5
5
|
"tools": {
|
|
6
6
|
"browser-session-bridge": {
|
|
7
7
|
"version": "0.1.0",
|
|
@@ -31,24 +31,26 @@
|
|
|
31
31
|
}
|
|
32
32
|
},
|
|
33
33
|
"campaign-draft-runner": {
|
|
34
|
-
"version": "0.
|
|
34
|
+
"version": "0.5.0",
|
|
35
35
|
"toolDependencies": {
|
|
36
36
|
"pr-campaign": "^0.1.0",
|
|
37
37
|
"gmail-workspace": "^0.1.0"
|
|
38
38
|
},
|
|
39
39
|
"files": {
|
|
40
|
-
"README.md": "
|
|
41
|
-
"
|
|
42
|
-
"
|
|
40
|
+
"README.md": "2c977357f774c6bc3369460ba9cd784c9b75b5f12b338e089e634e09f2a8cbcd",
|
|
41
|
+
"batch-skip.js": "ad428464b739e7355e00ac7cf399247ce611fcef0470d97a73893701e5b07627",
|
|
42
|
+
"config.js": "9b9c2d9686cebf17c3ba2630088c7028281883608f61054e733283aeb8d383cb",
|
|
43
|
+
"index.js": "669bf964a0e1e1a0bacc5997c6e49539022649f405543e5914ba01aa11a99330",
|
|
43
44
|
"operation-timeout.js": "9b1ced30ec9ed710bb9118b91f6f3feded5111a08e1e21abf163e4cfb91d7ad2",
|
|
44
45
|
"package.json": "a25b7ed1dd2c30cd68c3c10feb8ee1752cac8cd55cd4344690ffad649e231ec1",
|
|
45
46
|
"product-facts.js": "69339e20e7b70a6b9833878d72b10b70ea4ede4aa448097975d0961b6aaced59",
|
|
46
47
|
"search-quality.js": "ab5412d45d5144e976cccef52992e634c11aca0e21b73fd53f9dc2d489c56dd7",
|
|
47
48
|
"source-exhaustion.js": "407f940f5205eb01715a797672d0dad8bd0b0b897024b5fcb87052a062ecd054",
|
|
48
|
-
"telemetry.js": "
|
|
49
|
-
"test/
|
|
50
|
-
"test/
|
|
51
|
-
"
|
|
49
|
+
"telemetry.js": "984772e73d1cbb761771d6f62954a604333a301182ee74368a41ffab762da367",
|
|
50
|
+
"test/batch-skip.test.js": "638d185dc0b38900afe6ee9058e733ae36583d0762a0ff0347fdf245df064abc",
|
|
51
|
+
"test/selection-policy.test.js": "1283f18b861925aee810d156eb64c54b147304ff7352a8f83b9c2311e1f51de2",
|
|
52
|
+
"test/telemetry.test.js": "daa84b83658226ecd979f15d4e12d94740ffe5280d467ad29677fc1d77734a5b",
|
|
53
|
+
"tool.manifest.json": "71520f3be858b9c2193223e370c10d938484a2af9281087e6dc14410b45cb2ae"
|
|
52
54
|
}
|
|
53
55
|
},
|
|
54
56
|
"creator-scout": {
|
|
@@ -65,34 +67,39 @@
|
|
|
65
67
|
}
|
|
66
68
|
},
|
|
67
69
|
"gmail-workspace": {
|
|
68
|
-
"version": "0.1.
|
|
70
|
+
"version": "0.1.2",
|
|
69
71
|
"files": {
|
|
70
72
|
"config.js": "6ad295ec4cedbc936835d8f86c7528e0939ef09d5792c003308d2ee85dc5eea4",
|
|
71
|
-
"index.js": "
|
|
72
|
-
"package.json": "
|
|
73
|
+
"index.js": "729f345fbd82d8e9525d46412b96efac0a30387477385208a4bb1c83fd794992",
|
|
74
|
+
"package.json": "991b2a5c03a049faa9363444ff5fd7517fd3556228dd8bad3d0f554cd3e2dd73",
|
|
73
75
|
"secretary-state.js": "afc27732670420bccf1ce56ce715676dd2266de9f2d36a92015a93842755094a",
|
|
74
76
|
"test/secretary-state.test.mjs": "3aea05524a4ad5ebcb79c6aa1db94fcd009ac8ae69414c8f950b32a32af94370",
|
|
75
|
-
"tool.manifest.json": "
|
|
77
|
+
"tool.manifest.json": "1f9316a957b3a7167e41ece7c2b83f90e69fb390d6dd8c7f99789c538b7a4800",
|
|
78
|
+
"arguments.js": "b4089006818020cc1cd4e5a4cc6e22242939b45a4b42d216fba2269b8cb99e64",
|
|
79
|
+
"test/arguments.test.mjs": "dde82737924d75747490f5535bee2420c71838b3e758925132c825ee8c869bad"
|
|
76
80
|
}
|
|
77
81
|
},
|
|
78
82
|
"magnific-mcp": {
|
|
79
|
-
"version": "0.
|
|
83
|
+
"version": "0.4.1",
|
|
80
84
|
"toolDependencies": {
|
|
81
|
-
"mcp-client": "^0.
|
|
85
|
+
"mcp-client": "^0.2.0"
|
|
82
86
|
},
|
|
83
87
|
"files": {
|
|
84
|
-
"README.md": "
|
|
88
|
+
"README.md": "98327aacc89cadd761db2eb49f3892818c72d410f88b86a8b6fe904c4efb3630",
|
|
85
89
|
"config.js": "98a629781a1b2541806050952aa78a1e6e1b1671a6f1df7ba36c70dd116936a7",
|
|
86
90
|
"delivery-claims.js": "52f05a5bd1ece1ab21f7d9a49f362f9c55f101ef5723efd642dbecf0236921cd",
|
|
91
|
+
"generation-request.js": "a6daa889b68fa188111b4389c6446daba87fc46fbddcf4bcccba817cf3f0b607",
|
|
87
92
|
"generation-watch-close.js": "0dd780280eef870d2f11cb1800e6e9de19e5ead7b9d02b9983852cc8ddbdc79b",
|
|
88
93
|
"generation-watch-plan.js": "eb60660341f254adc224181ca19dbbb969ef1d676aeaab60b71a8567f52648fe",
|
|
89
|
-
"index.js": "
|
|
90
|
-
"magnific-api.js": "
|
|
94
|
+
"index.js": "2eead50413d076173648623285ac65977ec56d77b1275f3ff801c70f900f4bb5",
|
|
95
|
+
"magnific-api.js": "062ba561037760de347542b5bd7db5def1f940e1d8f147a131e5603dfccd22ab",
|
|
96
|
+
"media-output.js": "01aaaeffa64845a200261258dccedf3116f030fafd783db97e846582ec5c9a26",
|
|
91
97
|
"network.js": "b45e43b49eea98ca7d80921ebfa6daf8b0a85d2079d4653f9eda682bc82101ae",
|
|
92
|
-
"package.json": "
|
|
98
|
+
"package.json": "1842a0efe6021e197a14b54f6661fb9ccdcd26e92aa09a5ac50af6d3cee4367a",
|
|
93
99
|
"state-store.js": "c3ee744509f7a6be251cd895406bd1c70827f53cbdc1ac9d1c2a6e48479bfe1e",
|
|
94
|
-
"test/magnific-mcp.test.js": "
|
|
95
|
-
"
|
|
100
|
+
"test/magnific-mcp.test.js": "3af971e5a92cf6bd34111b159723892b3a5310952b036386ae98d3592708ddd0",
|
|
101
|
+
"test/media-output.test.js": "ba3802da6c3e7d3c6ef94fcceb10a0b8547921d0995aa24b8484ddb2fb1a28d9",
|
|
102
|
+
"tool.manifest.json": "7d26e0a101c8df81bcfdf502d6864a157f0e690e5636ce297e28b1ed07f7abc2"
|
|
96
103
|
}
|
|
97
104
|
},
|
|
98
105
|
"master-slave": {
|
|
@@ -132,35 +139,39 @@
|
|
|
132
139
|
}
|
|
133
140
|
},
|
|
134
141
|
"mcp-client": {
|
|
135
|
-
"version": "0.
|
|
142
|
+
"version": "0.2.0",
|
|
143
|
+
"toolDependencies": {},
|
|
136
144
|
"files": {
|
|
137
|
-
"README.md": "
|
|
145
|
+
"README.md": "f7317946d27742eebcc443d69e152c0f9f7e1d55b07192a3875cc1a0d623ce3a",
|
|
138
146
|
"config.js": "b5b05fb242034e2d04b6301e848da9701c95be195ba1cdac2421964e9a7db8c9",
|
|
139
|
-
"index.js": "
|
|
147
|
+
"index.js": "eaca434b3f0a7697955e0dd4cf4aa8dd7c59ab297c10f646b8cc4ec3189311e3",
|
|
140
148
|
"mcp-session.js": "51765b46f410e1cd49647235b611db0b10af20c9707efcacfbe0ff7dabe3c1ff",
|
|
141
149
|
"network-security.js": "60a020d4e3c7a4784a5a4e7b7246ee34e63ac73ee2316e09a09d10668dc8a7bc",
|
|
142
150
|
"oauth-device.js": "698aa9255bae47190c63527db7680aaab4f06392a108a0ff6444e32d31b7b89f",
|
|
143
151
|
"oauth-watch-plan.js": "b1bbed52eeb0e2e50dd52d733897ebd70573fea3ff5f023b3c63347f92633d3e",
|
|
144
152
|
"package-lock.json": "fd7b6bf4545de4056908bd656a4a4e21b9f84acc7b06d8c1d581a8c84c295e90",
|
|
145
|
-
"package.json": "
|
|
153
|
+
"package.json": "85ac369f959a40da258d268a3ab1d523abba7c225d5c44a903ddae0bcc512d1b",
|
|
146
154
|
"state-store.js": "a8fb63096b8efdec8de1cb386eff371f1552f3fd71d96f25036e1c724285e0af",
|
|
147
|
-
"test/mcp-client.test.js": "
|
|
148
|
-
"tool.
|
|
155
|
+
"test/mcp-client.test.js": "26a7fe3ffa33ad454eced32c7fab12975ac5c0914041dea68cb7e5467e753a10",
|
|
156
|
+
"tool-routing.js": "b3405c9879443cdc257a1e97d4968859a8654e62ff7cfb90ec3a7a992a62e311",
|
|
157
|
+
"tool.manifest.json": "2d64dc40453fba1d5a6743145f759a28b1f6d137fd84c0e15dd41745bd1692d7"
|
|
149
158
|
}
|
|
150
159
|
},
|
|
151
160
|
"official-tool-sync": {
|
|
152
|
-
"version": "1.
|
|
161
|
+
"version": "1.1.0",
|
|
153
162
|
"toolDependencies": {
|
|
154
163
|
"trash": "^1.0.0"
|
|
155
164
|
},
|
|
156
165
|
"files": {
|
|
157
166
|
".gitignore": "4d56952b0fb13bf8f9b6c13a6d4c34a075bac3af447636a1df4335d7576e2f97",
|
|
158
167
|
"config.js": "744c445b1d2ff65b77f38a5473c24e50a32f4288c4a08c25a874a478086c08c8",
|
|
159
|
-
"
|
|
160
|
-
"
|
|
168
|
+
"dependency-installer.js": "dd91072612d4cba2ce83748ffae569067cbb2ebf63d59084572c3a9b77611927",
|
|
169
|
+
"dependency-installer.test.js": "9dc8a8c7d3ea29cfa2bb2d4daef3c02a6e996f9148a27cc499c8fd628fac857f",
|
|
170
|
+
"index.js": "12186107d449868621af98ce8f434fc84b5de09c62a2cb6b2cb67219b2e856a6",
|
|
171
|
+
"package.json": "0e95a190a04ebaf1eea81a90ecdb7b260595eb1ea0afe77f4d4215ccd6a9e949",
|
|
161
172
|
"sync-lib.js": "c9f2cd7e19370088ab5fd10f6b2ada52c07d2e405674a356d0989f9a0f4984f6",
|
|
162
173
|
"sync-lib.test.js": "c97cf850d9f42f6b121a458863b048bdcaa6d7d0475642f20742c5862ebf7cc4",
|
|
163
|
-
"tool.manifest.json": "
|
|
174
|
+
"tool.manifest.json": "00086b52dc969275e6dffc70720ef916493870fff3a27d076fed4ec04a160c75"
|
|
164
175
|
}
|
|
165
176
|
},
|
|
166
177
|
"pr-campaign": {
|
|
@@ -189,18 +200,20 @@
|
|
|
189
200
|
}
|
|
190
201
|
},
|
|
191
202
|
"telemetry-ledger": {
|
|
192
|
-
"version": "0.1.
|
|
203
|
+
"version": "0.1.1",
|
|
193
204
|
"files": {
|
|
194
|
-
"README.md": "
|
|
205
|
+
"README.md": "e74bcf93bc89de151d73a03d60a74a2699436775aa5f93d27cf3dbb59250aec4",
|
|
195
206
|
"analysis.js": "c0effbff3af519b018e59c0460d2c277b4f7e1422ca2680de217bcb5e8a13136",
|
|
196
207
|
"config.js": "ddc8543cde5aeda634b8df60628d0024bb03e65b8710cf4878b4c6a7d953a641",
|
|
197
|
-
"index.js": "
|
|
198
|
-
"package.json": "
|
|
208
|
+
"index.js": "87f302af297233f2a1bc89d9be52b1b724478ed72658123ff841fcbcaa13389b",
|
|
209
|
+
"package.json": "f093c7b767dc042518ff6c19ac302b972a03ba34c45a73545079dd76bf525b75",
|
|
199
210
|
"stats.js": "fc41b065d1f280dc278c53dc4c4baaa385a5bf25416c3e4ff8a153ea624967fb",
|
|
200
211
|
"storage.js": "099187e270ccc2bd6d7300b78a2ee31a837f98694a23a3b40b20a646cdb1db1e",
|
|
201
212
|
"test/telemetry.test.js": "312eef22ad960b19a6fe777aecd520952ae20ed03168edbbef8a0f747bee01c0",
|
|
202
|
-
"tool.manifest.json": "
|
|
203
|
-
"validation.js": "1a50c75a5ca1bb7249bb8bfc3f2927b31ff4accd0efb56a48c36df5adae78f61"
|
|
213
|
+
"tool.manifest.json": "960b714ead9b0491581bf9f0f4dc882ce58bfc29cef4b2f5267cd0dbc9c37d48",
|
|
214
|
+
"validation.js": "1a50c75a5ca1bb7249bb8bfc3f2927b31ff4accd0efb56a48c36df5adae78f61",
|
|
215
|
+
"output.js": "e932098d51efdb99f8c39faaae0c5574d7e263f0e8c0875fc57e7a4311e715ea",
|
|
216
|
+
"test/output.test.js": "c18ae93cdf74de3748c1571833dfc1063ec6690219b03fbc7772bae504b6259f"
|
|
204
217
|
}
|
|
205
218
|
},
|
|
206
219
|
"trash": {
|
|
@@ -230,17 +243,18 @@
|
|
|
230
243
|
}
|
|
231
244
|
},
|
|
232
245
|
"x-dm": {
|
|
233
|
-
"version": "0.2.
|
|
246
|
+
"version": "0.2.1",
|
|
234
247
|
"toolDependencies": {
|
|
235
248
|
"browser-session-bridge": "^0.1.0"
|
|
236
249
|
},
|
|
237
250
|
"files": {
|
|
238
|
-
"config.js": "
|
|
239
|
-
"index.js": "
|
|
240
|
-
"package.json": "
|
|
251
|
+
"config.js": "ac6eb70c1bc94230bc6df89fc73fc22ac53ccab49926abc3c453f85aca220596",
|
|
252
|
+
"index.js": "d89f70fed0754c61bd7ce31739304e4512ddabf16bc79a1488c164e32d31672d",
|
|
253
|
+
"package.json": "af4d72a99318d04b522773b8489288d191210435ae5543b564e2ce747164386c",
|
|
241
254
|
"README.md": "503b0ed25ef9a8df3079523ea653178e6e029854b4d199cb8d20be82cc837f9e",
|
|
242
255
|
"test/x-dm.test.mjs": "06352996e14777358eb62ad5e5d757b0dab5c0696927ed54ff40a5b7cdefcfd0",
|
|
243
|
-
"tool.manifest.json": "
|
|
256
|
+
"tool.manifest.json": "227dc3c4a48948cf72544d4b99ba2c3c5a255f8425e795304881ede73d82d011",
|
|
257
|
+
"browser-cache.js": "f71c20f9ab5f04d9d46c5e2551b348d47baba89a10a07173070298098a6d5551"
|
|
244
258
|
}
|
|
245
259
|
}
|
|
246
260
|
}
|
|
@@ -114,7 +114,7 @@ export async function createApp({ logger, runtimeOverrides, requestRestart } = {
|
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
const artifactStore = new ArtifactStore();
|
|
117
|
-
const toolRegistry = new ToolRegistry({ logger });
|
|
117
|
+
const toolRegistry = new ToolRegistry({ logger, executionPolicy: config.toolExecution });
|
|
118
118
|
const toolProcessSupervisor = createToolProcessSupervisor({ logger, policy: config.daemons, toolRegistry });
|
|
119
119
|
const taskStore = new TaskStore();
|
|
120
120
|
await toolRegistry.load();
|
|
@@ -14,7 +14,7 @@ export async function createHeadlessApp({
|
|
|
14
14
|
configLoader = loadConfig,
|
|
15
15
|
artifactStoreFactory = () => new ArtifactStore(),
|
|
16
16
|
taskStoreFactory = () => new TaskStore(),
|
|
17
|
-
toolRegistryFactory = () => new ToolRegistry(
|
|
17
|
+
toolRegistryFactory = (options) => new ToolRegistry(options),
|
|
18
18
|
supervisorFactory = (options) => createToolProcessSupervisor(options),
|
|
19
19
|
capabilitiesFactory = (options) => createArisaCapabilities(options),
|
|
20
20
|
ipcServerFactory = (options) => createIpcServer(options)
|
|
@@ -23,7 +23,7 @@ export async function createHeadlessApp({
|
|
|
23
23
|
const config = await configLoader();
|
|
24
24
|
const artifactStore = artifactStoreFactory();
|
|
25
25
|
const taskStore = taskStoreFactory();
|
|
26
|
-
const toolRegistry = toolRegistryFactory();
|
|
26
|
+
const toolRegistry = toolRegistryFactory({ logger, executionPolicy: config.toolExecution });
|
|
27
27
|
await toolRegistry.load();
|
|
28
28
|
const toolProcessSupervisor = supervisorFactory({ logger, policy: config.daemons, toolRegistry });
|
|
29
29
|
const toolExecutor = createHeadlessToolExecutor({ artifactStore, taskStore, toolRegistry });
|
package/src/runtime/paths.js
CHANGED
|
@@ -65,6 +65,10 @@ export function getChatToolResourceNotesFile(chatId) {
|
|
|
65
65
|
return path.join(getChatDir(chatId), "state", "tool-resource-notes.json");
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
export function getChatTelegramWorkspacesFile(chatId) {
|
|
69
|
+
return path.join(getChatDir(chatId), "state", "telegram-workspaces.json");
|
|
70
|
+
}
|
|
71
|
+
|
|
68
72
|
export function getChatToolStateDir(chatId, toolName) {
|
|
69
73
|
return path.join(getChatDir(chatId), "state", "tools", requireToolName(toolName));
|
|
70
74
|
}
|
|
@@ -78,7 +78,9 @@ export async function handoffServiceRestart({ verbose = true, cliArgs = [] } = {
|
|
|
78
78
|
} = {}) {
|
|
79
79
|
await ensureHome();
|
|
80
80
|
const status = await getStatus();
|
|
81
|
-
|
|
81
|
+
const supervisorPid = Number.parseInt(environment.ARISA_SUPERVISOR_PID || "", 10);
|
|
82
|
+
const isActiveWorker = Number.isSafeInteger(supervisorPid) && supervisorPid === status.pid;
|
|
83
|
+
if (!status.running || (status.pid !== currentPid && !isActiveWorker)) {
|
|
82
84
|
throw new Error("Service restart handoff requires the active background service process");
|
|
83
85
|
}
|
|
84
86
|
const logHandle = await openLog(serviceLogFile, "a");
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
|
|
3
|
+
function sleep(ms) {
|
|
4
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function errorMessage(error) {
|
|
8
|
+
return error instanceof Error ? error.message : String(error);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function boundedInteger(value, fallback, minimum, maximum) {
|
|
12
|
+
const parsed = Number(value);
|
|
13
|
+
return Number.isSafeInteger(parsed) && parsed >= minimum ? Math.min(parsed, maximum) : fallback;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function createServiceSupervisor({
|
|
17
|
+
command,
|
|
18
|
+
args,
|
|
19
|
+
env = process.env,
|
|
20
|
+
restartLimit = 3,
|
|
21
|
+
restartBackoffMs = 2_000,
|
|
22
|
+
restartBackoffMaxMs = 60_000,
|
|
23
|
+
stableRuntimeMs = 60_000,
|
|
24
|
+
logger,
|
|
25
|
+
spawnProcess = spawn,
|
|
26
|
+
wait = sleep
|
|
27
|
+
}) {
|
|
28
|
+
const maximumRestarts = boundedInteger(restartLimit, 3, 0, 10);
|
|
29
|
+
const initialBackoffMs = boundedInteger(restartBackoffMs, 2_000, 1, 60_000);
|
|
30
|
+
const maximumBackoffMs = boundedInteger(restartBackoffMaxMs, 60_000, initialBackoffMs, 5 * 60_000);
|
|
31
|
+
const stableAfterMs = boundedInteger(stableRuntimeMs, 60_000, 1_000, 60 * 60_000);
|
|
32
|
+
let child = null;
|
|
33
|
+
let stopping = false;
|
|
34
|
+
let wakeBackoff = null;
|
|
35
|
+
let resolveDone;
|
|
36
|
+
const done = new Promise((resolve) => { resolveDone = resolve; });
|
|
37
|
+
|
|
38
|
+
async function run() {
|
|
39
|
+
let consecutiveFailures = 0;
|
|
40
|
+
while (!stopping) {
|
|
41
|
+
const startedAt = Date.now();
|
|
42
|
+
child = spawnProcess(command, args, {
|
|
43
|
+
stdio: "inherit",
|
|
44
|
+
env: { ...env, ARISA_SUPERVISOR_PID: String(process.pid) }
|
|
45
|
+
});
|
|
46
|
+
logger?.log("service", `worker started (pid ${child.pid})`);
|
|
47
|
+
|
|
48
|
+
const outcome = await new Promise((resolve) => {
|
|
49
|
+
child.once("error", (error) => resolve({ error }));
|
|
50
|
+
child.once("exit", (code, signal) => resolve({ code, signal }));
|
|
51
|
+
});
|
|
52
|
+
child = null;
|
|
53
|
+
if (stopping) break;
|
|
54
|
+
|
|
55
|
+
const runtimeMs = Date.now() - startedAt;
|
|
56
|
+
if (runtimeMs >= stableAfterMs) consecutiveFailures = 0;
|
|
57
|
+
consecutiveFailures += 1;
|
|
58
|
+
const detail = outcome.error
|
|
59
|
+
? errorMessage(outcome.error)
|
|
60
|
+
: `code=${outcome.code ?? "null"} signal=${outcome.signal || "none"}`;
|
|
61
|
+
logger?.error("service", `worker exited unexpectedly (${detail})`);
|
|
62
|
+
if (consecutiveFailures > maximumRestarts) {
|
|
63
|
+
logger?.error("service", `worker restart limit reached after ${consecutiveFailures} consecutive exits`);
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const delay = Math.min(maximumBackoffMs, initialBackoffMs * (2 ** (consecutiveFailures - 1)));
|
|
68
|
+
logger?.log("service", `restarting worker in ${delay}ms`);
|
|
69
|
+
await Promise.race([
|
|
70
|
+
wait(delay),
|
|
71
|
+
new Promise((resolve) => { wakeBackoff = resolve; })
|
|
72
|
+
]);
|
|
73
|
+
wakeBackoff = null;
|
|
74
|
+
}
|
|
75
|
+
resolveDone();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
async start() {
|
|
80
|
+
run().catch((error) => {
|
|
81
|
+
logger?.error("service", `supervisor failed: ${errorMessage(error)}`);
|
|
82
|
+
resolveDone();
|
|
83
|
+
});
|
|
84
|
+
return done;
|
|
85
|
+
},
|
|
86
|
+
async stop() {
|
|
87
|
+
stopping = true;
|
|
88
|
+
wakeBackoff?.();
|
|
89
|
+
if (child) {
|
|
90
|
+
const activeChild = child;
|
|
91
|
+
const exited = new Promise((resolve) => activeChild.once("exit", resolve));
|
|
92
|
+
activeChild.kill("SIGTERM");
|
|
93
|
+
await exited;
|
|
94
|
+
}
|
|
95
|
+
resolveDone();
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
}
|
|
@@ -18,6 +18,12 @@ import { createTelegramSessionBridgeController } from "./telegram-session-bridge
|
|
|
18
18
|
import { createTelegramToolsCommandHandler } from "./telegram-tools-command.js";
|
|
19
19
|
import { createTelegramWorkspaceController } from "./telegram-workspace-controller.js";
|
|
20
20
|
import { resolveTelegramWorkspaceRoute } from "./workspace-group.js";
|
|
21
|
+
import {
|
|
22
|
+
appendGeneralReplyRoutingInstruction,
|
|
23
|
+
isGeneralWorkspaceRoute,
|
|
24
|
+
routeGeneralWorkspaceReply
|
|
25
|
+
} from "./reply-topic-routing.js";
|
|
26
|
+
import { migrateLegacyReplyTopics, WorkspaceTopicStore } from "./workspace-topic-store.js";
|
|
21
27
|
import {
|
|
22
28
|
buildNewSessionPrompt,
|
|
23
29
|
buildPrompt,
|
|
@@ -174,6 +180,7 @@ export async function createTelegramBot({ config, artifactStore, toolRegistry, t
|
|
|
174
180
|
const bot = new Bot(config.telegram.token);
|
|
175
181
|
const perChatState = createChatStateStore();
|
|
176
182
|
const sessionSeeds = new SessionSeedStore();
|
|
183
|
+
const workspaceTopics = new WorkspaceTopicStore();
|
|
177
184
|
const notifiedPromptErrors = new WeakSet();
|
|
178
185
|
let taskTimer = null;
|
|
179
186
|
const { authorizeContext, contextRoute, registerRoute } = createTelegramWorkspaceController({
|
|
@@ -276,7 +283,13 @@ export async function createTelegramBot({ config, artifactStore, toolRegistry, t
|
|
|
276
283
|
if (normalizationRequired && !transcript) {
|
|
277
284
|
logger?.log("telegram", `media normalization unavailable for chat ${route.transportChatId}: ${toolResult?.error || toolResult?.missingConfig?.join(", ") || "unknown error"}`);
|
|
278
285
|
}
|
|
279
|
-
|
|
286
|
+
const prompt = buildPrompt({ ctx, artifact, transcript, toolResult });
|
|
287
|
+
if (!isGeneralWorkspaceRoute(route)) return prompt;
|
|
288
|
+
const [topics, recentProposals] = await Promise.all([
|
|
289
|
+
workspaceTopics.listTopics(route.ownerChatId, route.transportChatId),
|
|
290
|
+
workspaceTopics.listRecentProposals(route.ownerChatId, route.transportChatId)
|
|
291
|
+
]);
|
|
292
|
+
return appendGeneralReplyRoutingInstruction(prompt, topics, recentProposals);
|
|
280
293
|
}
|
|
281
294
|
|
|
282
295
|
const {
|
|
@@ -290,6 +303,7 @@ export async function createTelegramBot({ config, artifactStore, toolRegistry, t
|
|
|
290
303
|
agentManager,
|
|
291
304
|
artifactStore,
|
|
292
305
|
sessionSeeds,
|
|
306
|
+
workspaceTopics,
|
|
293
307
|
getChatState,
|
|
294
308
|
buildTopicInitializationHandoff,
|
|
295
309
|
logger
|
|
@@ -354,13 +368,29 @@ export async function createTelegramBot({ config, artifactStore, toolRegistry, t
|
|
|
354
368
|
}
|
|
355
369
|
executionReceipt?.resolve({ status: "executed" });
|
|
356
370
|
if (text) {
|
|
357
|
-
|
|
371
|
+
const topics = route.workspace && route.ownerChatId
|
|
372
|
+
? await workspaceTopics.listTopics(route.ownerChatId, route.transportChatId)
|
|
373
|
+
: [];
|
|
374
|
+
const routedReply = routeGeneralWorkspaceReply({ route, text, topics });
|
|
375
|
+
if (routedReply.proposal) {
|
|
376
|
+
await workspaceTopics.recordProposal(route.ownerChatId, route.transportChatId, routedReply.proposal);
|
|
377
|
+
logger?.log("telegram", `recorded topic proposal ${routedReply.proposal} for workspace ${route.transportChatId}`);
|
|
378
|
+
}
|
|
379
|
+
if (!routedReply.text) return;
|
|
380
|
+
const deliveryRoute = routedReply.route;
|
|
381
|
+
const deliveryOptions = (extra = {}) => deliveryRoute.workspace && deliveryRoute.threadId
|
|
382
|
+
? { ...extra, message_thread_id: deliveryRoute.threadId }
|
|
383
|
+
: extra;
|
|
384
|
+
await createWorkspaceAccessGuard(deliveryRoute)();
|
|
385
|
+
if (routedReply.topic) {
|
|
386
|
+
logger?.log("telegram", `routing General reply to topic ${routedReply.topic.threadId} (${routedReply.topic.name})`);
|
|
387
|
+
}
|
|
358
388
|
await sendTextReply({
|
|
359
|
-
sendText: (message, extra) => bot.api.sendMessage(
|
|
360
|
-
sendDocument: (file, extra) => bot.api.sendDocument(
|
|
389
|
+
sendText: (message, extra) => bot.api.sendMessage(deliveryRoute.transportChatId, message, deliveryOptions(extra)),
|
|
390
|
+
sendDocument: (file, extra) => bot.api.sendDocument(deliveryRoute.transportChatId, file, deliveryOptions(extra)),
|
|
361
391
|
chatId: sessionId,
|
|
362
|
-
artifactChatId:
|
|
363
|
-
text
|
|
392
|
+
artifactChatId: deliveryRoute.scopeChatId,
|
|
393
|
+
text: routedReply.text
|
|
364
394
|
});
|
|
365
395
|
}
|
|
366
396
|
};
|
|
@@ -377,10 +407,11 @@ export async function createTelegramBot({ config, artifactStore, toolRegistry, t
|
|
|
377
407
|
replaceQueued = false,
|
|
378
408
|
busyMessageMode = "queue",
|
|
379
409
|
waitForExecution = false,
|
|
410
|
+
onExecutionStart = null,
|
|
380
411
|
coalesceQueued = false
|
|
381
412
|
}) {
|
|
382
413
|
const chatState = getChatState(chatId);
|
|
383
|
-
const receipt = waitForExecution ? createPromptExecutionReceipt() : null;
|
|
414
|
+
const receipt = waitForExecution ? createPromptExecutionReceipt(onExecutionStart) : null;
|
|
384
415
|
|
|
385
416
|
if (chatState.processing) {
|
|
386
417
|
const incomingRoute = ctx ? contextRoute(ctx) : null;
|
|
@@ -474,6 +505,13 @@ export async function createTelegramBot({ config, artifactStore, toolRegistry, t
|
|
|
474
505
|
});
|
|
475
506
|
}
|
|
476
507
|
|
|
508
|
+
async function migrateConfiguredReplyTopics() {
|
|
509
|
+
const migratedGroups = await migrateLegacyReplyTopics(config, workspaceTopics);
|
|
510
|
+
if (!migratedGroups) return;
|
|
511
|
+
await saveConfig(config);
|
|
512
|
+
logger?.log("telegram", `migrated configured reply topics for ${migratedGroups} workspace group(s)`);
|
|
513
|
+
}
|
|
514
|
+
|
|
477
515
|
async function sendStartupMessages() {
|
|
478
516
|
for (const chatId of config.telegram.authorizedChatIds || []) {
|
|
479
517
|
try {
|
|
@@ -505,7 +543,7 @@ export async function createTelegramBot({ config, artifactStore, toolRegistry, t
|
|
|
505
543
|
timer.unref?.();
|
|
506
544
|
}
|
|
507
545
|
|
|
508
|
-
async function enqueueAsyncPrompt({ chatId, prompt, label, route: taskRoute }) {
|
|
546
|
+
async function enqueueAsyncPrompt({ chatId, prompt, label, route: taskRoute, timeoutMs }) {
|
|
509
547
|
let ctx = { chat: { id: chatId }, api: bot.api };
|
|
510
548
|
const destination = taskRoute?.transport === "telegram" ? taskRoute.destination : null;
|
|
511
549
|
if (destination?.chatId && destination?.threadId) {
|
|
@@ -522,7 +560,32 @@ export async function createTelegramBot({ config, artifactStore, toolRegistry, t
|
|
|
522
560
|
const route = contextRoute(ctx);
|
|
523
561
|
const chatState = getChatState(route.sessionId);
|
|
524
562
|
if (chatState.processing) await ensureQueuedTelegramTyping(chatState, ctx);
|
|
525
|
-
|
|
563
|
+
let timer = null;
|
|
564
|
+
const execution = enqueuePrompt({
|
|
565
|
+
chatId: route.sessionId,
|
|
566
|
+
prompt,
|
|
567
|
+
label,
|
|
568
|
+
ctx,
|
|
569
|
+
waitForExecution: true,
|
|
570
|
+
onExecutionStart: ({ reject }) => {
|
|
571
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) return;
|
|
572
|
+
timer = setTimeout(() => {
|
|
573
|
+
const error = new Error(`${label} exceeded its ${timeoutMs}ms execution deadline`);
|
|
574
|
+
error.retryable = false;
|
|
575
|
+
error.outcomeUncertain = true;
|
|
576
|
+
reject(error);
|
|
577
|
+
agentManager.abortSession(route.sessionId).catch((abortError) => {
|
|
578
|
+
logger?.error("tasks", `${label} abort failed: ${getErrorMessage(abortError)}`);
|
|
579
|
+
});
|
|
580
|
+
}, timeoutMs);
|
|
581
|
+
timer.unref?.();
|
|
582
|
+
}
|
|
583
|
+
});
|
|
584
|
+
execution.then(
|
|
585
|
+
() => { if (timer) clearTimeout(timer); },
|
|
586
|
+
() => { if (timer) clearTimeout(timer); }
|
|
587
|
+
);
|
|
588
|
+
return execution;
|
|
526
589
|
}
|
|
527
590
|
|
|
528
591
|
const { dispatchDueTasks } = createTelegramTaskDispatcher({
|
|
@@ -533,6 +596,7 @@ export async function createTelegramBot({ config, artifactStore, toolRegistry, t
|
|
|
533
596
|
toolRegistry,
|
|
534
597
|
resourceNotes,
|
|
535
598
|
agentManager,
|
|
599
|
+
taskTimeouts: config.tasks,
|
|
536
600
|
logger
|
|
537
601
|
});
|
|
538
602
|
|
|
@@ -737,6 +801,9 @@ export async function createTelegramBot({ config, artifactStore, toolRegistry, t
|
|
|
737
801
|
bot.on("message", async (ctx) => {
|
|
738
802
|
const auth = await authorizeContext(ctx);
|
|
739
803
|
if (!auth.ok) return;
|
|
804
|
+
await workspaceTopics.observeMessage(contextRoute(ctx), ctx.message).catch((error) => {
|
|
805
|
+
logger?.error("telegram", `workspace topic observation failed: ${getErrorMessage(error)}`);
|
|
806
|
+
});
|
|
740
807
|
if (!isProcessableTelegramMessage(ctx.message)) return;
|
|
741
808
|
|
|
742
809
|
const command = getTelegramCommand(ctx);
|
|
@@ -765,6 +832,7 @@ export async function createTelegramBot({ config, artifactStore, toolRegistry, t
|
|
|
765
832
|
return {
|
|
766
833
|
async start({ skipAgentStartupPrompts = false } = {}) {
|
|
767
834
|
config.telegram.chatMeta ||= {};
|
|
835
|
+
await migrateConfiguredReplyTopics();
|
|
768
836
|
await bot.api.setMyCommands(telegramCommands);
|
|
769
837
|
if (!taskTimer) {
|
|
770
838
|
taskTimer = setInterval(() => {
|
|
@@ -31,14 +31,24 @@ export function createChatStateStore() {
|
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
export function createPromptExecutionReceipt() {
|
|
34
|
+
export function createPromptExecutionReceipt(onStart = null) {
|
|
35
35
|
let resolve;
|
|
36
36
|
let reject;
|
|
37
|
+
let started = false;
|
|
37
38
|
const promise = new Promise((resolvePromise, rejectPromise) => {
|
|
38
39
|
resolve = resolvePromise;
|
|
39
40
|
reject = rejectPromise;
|
|
40
41
|
});
|
|
41
|
-
return {
|
|
42
|
+
return {
|
|
43
|
+
promise,
|
|
44
|
+
resolve,
|
|
45
|
+
reject,
|
|
46
|
+
start() {
|
|
47
|
+
if (started) return;
|
|
48
|
+
started = true;
|
|
49
|
+
onStart?.({ resolve, reject, promise });
|
|
50
|
+
}
|
|
51
|
+
};
|
|
42
52
|
}
|
|
43
53
|
|
|
44
54
|
function rejectQueuedReceipts(chatState, error) {
|
|
@@ -169,6 +179,7 @@ export async function drainChatPromptQueue({
|
|
|
169
179
|
chatState.continueAfterClose = false;
|
|
170
180
|
}
|
|
171
181
|
try {
|
|
182
|
+
currentReceipt?.start?.();
|
|
172
183
|
await processPrompt({ prompt: currentPrompt, ctx: currentCtx, receipt: currentReceipt });
|
|
173
184
|
currentReceipt?.resolve({ status: "completed" });
|
|
174
185
|
} catch (error) {
|