opensquid 0.5.441 → 0.5.447
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 +1 -0
- package/dist/functions/arm_scope.d.ts +27 -0
- package/dist/functions/arm_scope.d.ts.map +1 -0
- package/dist/functions/arm_scope.js +52 -0
- package/dist/functions/arm_scope.js.map +1 -0
- package/dist/functions/index.d.ts +1 -0
- package/dist/functions/index.d.ts.map +1 -1
- package/dist/functions/index.js +1 -0
- package/dist/functions/index.js.map +1 -1
- package/dist/runtime/bootstrap.d.ts.map +1 -1
- package/dist/runtime/bootstrap.js +2 -0
- package/dist/runtime/bootstrap.js.map +1 -1
- package/dist/runtime/handoff/render.d.ts +5 -4
- package/dist/runtime/handoff/render.d.ts.map +1 -1
- package/dist/runtime/handoff/render.js +7 -7
- package/dist/runtime/handoff/render.js.map +1 -1
- package/dist/runtime/hooks/active_task_mirror.js +0 -0
- package/dist/runtime/hooks/apply_patch.js +0 -0
- package/dist/runtime/hooks/dispatch.js +0 -0
- package/dist/runtime/hooks/hook_output.js +0 -0
- package/dist/runtime/hooks/memory_reconcile.js +0 -0
- package/dist/runtime/hooks/new_project_detect.js +0 -0
- package/dist/runtime/hooks/profession_resolver.js +0 -0
- package/dist/runtime/hooks/scope_intent.js +0 -0
- package/dist/runtime/hooks/session_id.js +0 -0
- package/dist/runtime/hooks/session_liveness.js +0 -0
- package/dist/runtime/hooks/stop_drive.js +0 -0
- package/dist/runtime/hooks/stop_stream.js +0 -0
- package/dist/runtime/hooks/subagent_guard.js +0 -0
- package/dist/runtime/hooks/transcript.js +0 -0
- package/dist/runtime/hooks/transcript_tasks.js +0 -0
- package/dist/runtime/ralph/orchestrator.d.ts.map +1 -1
- package/dist/runtime/ralph/orchestrator.js +2 -1
- package/dist/runtime/ralph/orchestrator.js.map +1 -1
- package/dist/setup/cli/limits_state.d.ts.map +1 -1
- package/dist/setup/cli/limits_state.js +6 -40
- package/dist/setup/cli/limits_state.js.map +1 -1
- package/dist/setup/cli/pack_walk.d.ts +32 -0
- package/dist/setup/cli/pack_walk.d.ts.map +1 -0
- package/dist/setup/cli/pack_walk.js +76 -0
- package/dist/setup/cli/pack_walk.js.map +1 -0
- package/dist/setup/cli/permissions_state.d.ts.map +1 -1
- package/dist/setup/cli/permissions_state.js +6 -37
- package/dist/setup/cli/permissions_state.js.map +1 -1
- package/dist/setup/cli/triggers_state.d.ts.map +1 -1
- package/dist/setup/cli/triggers_state.js +3 -29
- package/dist/setup/cli/triggers_state.js.map +1 -1
- package/dist/workgraph/events.d.ts.map +1 -1
- package/dist/workgraph/events.js +10 -0
- package/dist/workgraph/events.js.map +1 -1
- package/dist/workgraph/store.d.ts.map +1 -1
- package/dist/workgraph/store.js +5 -0
- package/dist/workgraph/store.js.map +1 -1
- package/dist/workgraph/types.d.ts +2 -1
- package/dist/workgraph/types.d.ts.map +1 -1
- package/docs/ARCHITECTURE.md +268 -0
- package/package.json +5 -3
- package/packs/builtin/coding-flow/skills/entry-and-handoffs/skill.yaml +13 -17
- package/dist/anti-drift/evaluator.d.ts +0 -88
- package/dist/anti-drift/evaluator.d.ts.map +0 -1
- package/dist/anti-drift/evaluator.js +0 -417
- package/dist/anti-drift/evaluator.js.map +0 -1
- package/dist/anti-drift/evaluator.test.js +0 -78
- package/dist/anti-drift/rules.d.ts +0 -80
- package/dist/anti-drift/rules.d.ts.map +0 -1
- package/dist/anti-drift/rules.js +0 -368
- package/dist/anti-drift/rules.js.map +0 -1
- package/dist/anti-drift/rules.test.js +0 -213
- package/dist/anti-drift/state.d.ts +0 -107
- package/dist/anti-drift/state.d.ts.map +0 -1
- package/dist/anti-drift/state.js +0 -177
- package/dist/anti-drift/state.js.map +0 -1
- package/dist/anti-drift/state.test.js +0 -120
- package/dist/chat/adapters/discord.d.ts +0 -41
- package/dist/chat/adapters/discord.d.ts.map +0 -1
- package/dist/chat/adapters/discord.js +0 -176
- package/dist/chat/adapters/discord.js.map +0 -1
- package/dist/chat/adapters/discord.test.js +0 -25
- package/dist/chat/adapters/slack.d.ts +0 -43
- package/dist/chat/adapters/slack.d.ts.map +0 -1
- package/dist/chat/adapters/slack.js +0 -172
- package/dist/chat/adapters/slack.js.map +0 -1
- package/dist/chat/adapters/slack.test.js +0 -30
- package/dist/chat/adapters/telegram.d.ts +0 -148
- package/dist/chat/adapters/telegram.d.ts.map +0 -1
- package/dist/chat/adapters/telegram.js +0 -498
- package/dist/chat/adapters/telegram.js.map +0 -1
- package/dist/chat/adapters/telegram.test.js +0 -94
- package/dist/chat/config.d.ts +0 -98
- package/dist/chat/config.d.ts.map +0 -1
- package/dist/chat/config.js +0 -185
- package/dist/chat/config.js.map +0 -1
- package/dist/chat/daemon/active-project.d.ts +0 -17
- package/dist/chat/daemon/active-project.d.ts.map +0 -1
- package/dist/chat/daemon/active-project.js +0 -23
- package/dist/chat/daemon/active-project.js.map +0 -1
- package/dist/chat/daemon/autospawn.d.ts +0 -40
- package/dist/chat/daemon/autospawn.d.ts.map +0 -1
- package/dist/chat/daemon/autospawn.js +0 -129
- package/dist/chat/daemon/autospawn.js.map +0 -1
- package/dist/chat/daemon/autospawn.test.js +0 -112
- package/dist/chat/daemon/cli.d.ts +0 -18
- package/dist/chat/daemon/cli.d.ts.map +0 -1
- package/dist/chat/daemon/cli.js +0 -71
- package/dist/chat/daemon/cli.js.map +0 -1
- package/dist/chat/daemon/collisions.js +0 -384
- package/dist/chat/daemon/health-check.d.ts +0 -69
- package/dist/chat/daemon/health-check.d.ts.map +0 -1
- package/dist/chat/daemon/health-check.js +0 -112
- package/dist/chat/daemon/health-check.js.map +0 -1
- package/dist/chat/daemon/inbox-read.d.ts +0 -35
- package/dist/chat/daemon/inbox-read.d.ts.map +0 -1
- package/dist/chat/daemon/inbox-read.js +0 -75
- package/dist/chat/daemon/inbox-read.js.map +0 -1
- package/dist/chat/daemon/inbox-read.test.js +0 -97
- package/dist/chat/daemon/inbox.d.ts +0 -63
- package/dist/chat/daemon/inbox.d.ts.map +0 -1
- package/dist/chat/daemon/inbox.js +0 -56
- package/dist/chat/daemon/inbox.js.map +0 -1
- package/dist/chat/daemon/inbox.test.js +0 -110
- package/dist/chat/daemon/lifecycle.d.ts +0 -71
- package/dist/chat/daemon/lifecycle.d.ts.map +0 -1
- package/dist/chat/daemon/lifecycle.js +0 -221
- package/dist/chat/daemon/lifecycle.js.map +0 -1
- package/dist/chat/daemon/lifecycle.test.js +0 -163
- package/dist/chat/daemon/protocol.d.ts +0 -107
- package/dist/chat/daemon/protocol.d.ts.map +0 -1
- package/dist/chat/daemon/protocol.js +0 -54
- package/dist/chat/daemon/protocol.js.map +0 -1
- package/dist/chat/daemon/routing.d.ts +0 -140
- package/dist/chat/daemon/routing.d.ts.map +0 -1
- package/dist/chat/daemon/routing.js +0 -198
- package/dist/chat/daemon/routing.js.map +0 -1
- package/dist/chat/daemon/routing.test.js +0 -259
- package/dist/chat/daemon/rpc-client.d.ts +0 -45
- package/dist/chat/daemon/rpc-client.d.ts.map +0 -1
- package/dist/chat/daemon/rpc-client.js +0 -133
- package/dist/chat/daemon/rpc-client.js.map +0 -1
- package/dist/chat/daemon/rpc-server.d.ts +0 -39
- package/dist/chat/daemon/rpc-server.d.ts.map +0 -1
- package/dist/chat/daemon/rpc-server.js +0 -385
- package/dist/chat/daemon/rpc-server.js.map +0 -1
- package/dist/chat/daemon/rpc.test.js +0 -177
- package/dist/chat/daemon/subscribers.js +0 -257
- package/dist/chat/daemon/worker.d.ts +0 -27
- package/dist/chat/daemon/worker.d.ts.map +0 -1
- package/dist/chat/daemon/worker.js +0 -313
- package/dist/chat/daemon/worker.js.map +0 -1
- package/dist/chat/daemon/workspace-topic.js +0 -324
- package/dist/chat/env-token.d.ts +0 -60
- package/dist/chat/env-token.d.ts.map +0 -1
- package/dist/chat/env-token.js +0 -137
- package/dist/chat/env-token.js.map +0 -1
- package/dist/chat/env-token.test.js +0 -160
- package/dist/chat/factory.d.ts +0 -30
- package/dist/chat/factory.d.ts.map +0 -1
- package/dist/chat/factory.js +0 -50
- package/dist/chat/factory.js.map +0 -1
- package/dist/chat/factory.test.js +0 -55
- package/dist/chat/gateway.d.ts +0 -176
- package/dist/chat/gateway.d.ts.map +0 -1
- package/dist/chat/gateway.js +0 -146
- package/dist/chat/gateway.js.map +0 -1
- package/dist/chat/gateway.test.js +0 -192
- package/dist/claude-md.d.ts +0 -39
- package/dist/claude-md.d.ts.map +0 -1
- package/dist/claude-md.js +0 -113
- package/dist/claude-md.js.map +0 -1
- package/dist/claude-md.test.js +0 -91
- package/dist/codex/activate.d.ts +0 -66
- package/dist/codex/activate.d.ts.map +0 -1
- package/dist/codex/activate.js +0 -329
- package/dist/codex/activate.js.map +0 -1
- package/dist/codex/activate.test.js +0 -229
- package/dist/codex/bundled-default/bundled-default.test.js +0 -161
- package/dist/codex/cli-publish.test.js +0 -133
- package/dist/codex/cli.d.ts +0 -35
- package/dist/codex/cli.d.ts.map +0 -1
- package/dist/codex/cli.js +0 -554
- package/dist/codex/cli.js.map +0 -1
- package/dist/codex/cli.test.js +0 -277
- package/dist/codex/import-skill-md.d.ts +0 -53
- package/dist/codex/import-skill-md.d.ts.map +0 -1
- package/dist/codex/import-skill-md.js +0 -236
- package/dist/codex/import-skill-md.js.map +0 -1
- package/dist/codex/import-skill-md.test.js +0 -225
- package/dist/codex/loader.d.ts +0 -27
- package/dist/codex/loader.d.ts.map +0 -1
- package/dist/codex/loader.js +0 -86
- package/dist/codex/loader.js.map +0 -1
- package/dist/codex/loader.test.js +0 -75
- package/dist/codex/parse.d.ts +0 -28
- package/dist/codex/parse.d.ts.map +0 -1
- package/dist/codex/parse.js +0 -309
- package/dist/codex/parse.js.map +0 -1
- package/dist/codex/parse.test.js +0 -241
- package/dist/codex/store.d.ts +0 -87
- package/dist/codex/store.d.ts.map +0 -1
- package/dist/codex/store.js +0 -205
- package/dist/codex/store.js.map +0 -1
- package/dist/codex/store.test.js +0 -242
- package/dist/codex/types.d.ts +0 -398
- package/dist/codex/types.d.ts.map +0 -1
- package/dist/codex/types.js +0 -21
- package/dist/codex/types.js.map +0 -1
- package/dist/config.d.ts +0 -53
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js +0 -202
- package/dist/config.js.map +0 -1
- package/dist/config.test.js +0 -117
- package/dist/engine/cli.d.ts +0 -14
- package/dist/engine/cli.d.ts.map +0 -1
- package/dist/engine/cli.js +0 -171
- package/dist/engine/cli.js.map +0 -1
- package/dist/engine/client.d.ts +0 -219
- package/dist/engine/client.d.ts.map +0 -1
- package/dist/engine/client.js +0 -312
- package/dist/engine/client.js.map +0 -1
- package/dist/engine/config.d.ts +0 -62
- package/dist/engine/config.d.ts.map +0 -1
- package/dist/engine/config.js +0 -223
- package/dist/engine/config.js.map +0 -1
- package/dist/engine/index.d.ts +0 -17
- package/dist/engine/index.d.ts.map +0 -1
- package/dist/engine/index.js +0 -16
- package/dist/engine/index.js.map +0 -1
- package/dist/engine/resolver.d.ts +0 -62
- package/dist/engine/resolver.d.ts.map +0 -1
- package/dist/engine/resolver.js +0 -103
- package/dist/engine/resolver.js.map +0 -1
- package/dist/engine/singleton.d.ts +0 -95
- package/dist/engine/singleton.d.ts.map +0 -1
- package/dist/engine/singleton.js +0 -325
- package/dist/engine/singleton.js.map +0 -1
- package/dist/engine/types.d.ts +0 -402
- package/dist/engine/types.d.ts.map +0 -1
- package/dist/engine/types.js +0 -22
- package/dist/engine/types.js.map +0 -1
- package/dist/engine-binary-resolver.js +0 -110
- package/dist/engine-binary-resolver.test.js +0 -61
- package/dist/engine-cli.js +0 -60
- package/dist/engine-client.js +0 -301
- package/dist/engine-client.test.js +0 -118
- package/dist/functions/chain_state.d.ts +0 -51
- package/dist/functions/chain_state.d.ts.map +0 -1
- package/dist/functions/chain_state.js +0 -59
- package/dist/functions/chain_state.js.map +0 -1
- package/dist/hooks/drift-catalog.d.ts +0 -68
- package/dist/hooks/drift-catalog.d.ts.map +0 -1
- package/dist/hooks/drift-catalog.js +0 -184
- package/dist/hooks/drift-catalog.js.map +0 -1
- package/dist/hooks/drift-catalog.test.js +0 -154
- package/dist/hooks/drift-patterns.d.ts +0 -110
- package/dist/hooks/drift-patterns.d.ts.map +0 -1
- package/dist/hooks/drift-patterns.js +0 -289
- package/dist/hooks/drift-patterns.js.map +0 -1
- package/dist/hooks/drift-patterns.test.js +0 -325
- package/dist/hooks/engine-vocab-gate.d.ts +0 -108
- package/dist/hooks/engine-vocab-gate.d.ts.map +0 -1
- package/dist/hooks/engine-vocab-gate.js +0 -225
- package/dist/hooks/engine-vocab-gate.js.map +0 -1
- package/dist/hooks/engine-vocab-gate.test.js +0 -170
- package/dist/hooks/heartbeat.d.ts +0 -107
- package/dist/hooks/heartbeat.d.ts.map +0 -1
- package/dist/hooks/heartbeat.js +0 -316
- package/dist/hooks/heartbeat.js.map +0 -1
- package/dist/hooks/heartbeat.test.js +0 -393
- package/dist/hooks/honesty-ledger-session-scope.test.js +0 -100
- package/dist/hooks/honesty-ledger.d.ts +0 -123
- package/dist/hooks/honesty-ledger.d.ts.map +0 -1
- package/dist/hooks/honesty-ledger.js +0 -226
- package/dist/hooks/honesty-ledger.js.map +0 -1
- package/dist/hooks/honesty-ledger.test.js +0 -466
- package/dist/hooks/inline-report-check.d.ts +0 -63
- package/dist/hooks/inline-report-check.d.ts.map +0 -1
- package/dist/hooks/inline-report-check.js +0 -88
- package/dist/hooks/inline-report-check.js.map +0 -1
- package/dist/hooks/inline-report-check.test.js +0 -96
- package/dist/hooks/pre-tool-use.d.ts +0 -62
- package/dist/hooks/pre-tool-use.d.ts.map +0 -1
- package/dist/hooks/pre-tool-use.js +0 -342
- package/dist/hooks/pre-tool-use.js.map +0 -1
- package/dist/hooks/pre-tool-use.test.js +0 -134
- package/dist/hooks/session-end.d.ts +0 -15
- package/dist/hooks/session-end.d.ts.map +0 -1
- package/dist/hooks/session-end.js +0 -60
- package/dist/hooks/session-end.js.map +0 -1
- package/dist/hooks/session-end.test.js +0 -52
- package/dist/hooks/stop.d.ts +0 -35
- package/dist/hooks/stop.d.ts.map +0 -1
- package/dist/hooks/stop.js +0 -136
- package/dist/hooks/stop.js.map +0 -1
- package/dist/hooks/transcript-active-task.test.js +0 -342
- package/dist/hooks/transcript.d.ts +0 -26
- package/dist/hooks/transcript.d.ts.map +0 -1
- package/dist/hooks/transcript.js +0 -266
- package/dist/hooks/transcript.js.map +0 -1
- package/dist/hooks/transcript.test.js +0 -103
- package/dist/hooks/user-prompt-submit.d.ts +0 -74
- package/dist/hooks/user-prompt-submit.d.ts.map +0 -1
- package/dist/hooks/user-prompt-submit.js +0 -256
- package/dist/hooks/user-prompt-submit.js.map +0 -1
- package/dist/hooks/user-prompt-submit.test.js +0 -118
- package/dist/hooks/versioning-gate.d.ts +0 -101
- package/dist/hooks/versioning-gate.d.ts.map +0 -1
- package/dist/hooks/versioning-gate.js +0 -245
- package/dist/hooks/versioning-gate.js.map +0 -1
- package/dist/hooks/versioning-gate.test.js +0 -368
- package/dist/hooks/workflow-gate.d.ts +0 -64
- package/dist/hooks/workflow-gate.d.ts.map +0 -1
- package/dist/hooks/workflow-gate.js +0 -152
- package/dist/hooks/workflow-gate.js.map +0 -1
- package/dist/hooks/workflow-gate.test.js +0 -197
- package/dist/hooks-cli.d.ts +0 -25
- package/dist/hooks-cli.d.ts.map +0 -1
- package/dist/hooks-cli.js +0 -286
- package/dist/hooks-cli.js.map +0 -1
- package/dist/hooks-cli.test.js +0 -148
- package/dist/origin.d.ts +0 -16
- package/dist/origin.d.ts.map +0 -1
- package/dist/origin.js +0 -92
- package/dist/origin.js.map +0 -1
- package/dist/packs/seed_lessons_ingest.d.ts +0 -30
- package/dist/packs/seed_lessons_ingest.d.ts.map +0 -1
- package/dist/packs/seed_lessons_ingest.js +0 -107
- package/dist/packs/seed_lessons_ingest.js.map +0 -1
- package/dist/project-cli.d.ts +0 -7
- package/dist/project-cli.d.ts.map +0 -1
- package/dist/project-cli.js +0 -145
- package/dist/project-cli.js.map +0 -1
- package/dist/project.d.ts +0 -127
- package/dist/project.d.ts.map +0 -1
- package/dist/project.js +0 -281
- package/dist/project.js.map +0 -1
- package/dist/project.test.js +0 -287
- package/dist/rag/backends/loop_engine.d.ts +0 -61
- package/dist/rag/backends/loop_engine.d.ts.map +0 -1
- package/dist/rag/backends/loop_engine.js +0 -160
- package/dist/rag/backends/loop_engine.js.map +0 -1
- package/dist/recall.d.ts +0 -82
- package/dist/recall.d.ts.map +0 -1
- package/dist/recall.js +0 -81
- package/dist/recall.js.map +0 -1
- package/dist/runtime/agent_bridge/autospawn.d.ts +0 -131
- package/dist/runtime/agent_bridge/autospawn.d.ts.map +0 -1
- package/dist/runtime/agent_bridge/autospawn.js +0 -251
- package/dist/runtime/agent_bridge/autospawn.js.map +0 -1
- package/dist/runtime/chain_state.d.ts +0 -124
- package/dist/runtime/chain_state.d.ts.map +0 -1
- package/dist/runtime/chain_state.js +0 -189
- package/dist/runtime/chain_state.js.map +0 -1
- package/dist/runtime/hooks/permission_decision.d.ts +0 -34
- package/dist/runtime/hooks/permission_decision.d.ts.map +0 -1
- package/dist/runtime/hooks/permission_decision.js +0 -39
- package/dist/runtime/hooks/permission_decision.js.map +0 -1
- package/dist/runtime/workflow_fsm.d.ts +0 -21
- package/dist/runtime/workflow_fsm.d.ts.map +0 -1
- package/dist/runtime/workflow_fsm.js +0 -25
- package/dist/runtime/workflow_fsm.js.map +0 -1
- package/dist/runtime/workflow_map.d.ts +0 -26
- package/dist/runtime/workflow_map.d.ts.map +0 -1
- package/dist/runtime/workflow_map.js +0 -38
- package/dist/runtime/workflow_map.js.map +0 -1
- package/dist/scope.d.ts +0 -48
- package/dist/scope.d.ts.map +0 -1
- package/dist/scope.js +0 -111
- package/dist/scope.js.map +0 -1
- package/dist/setup/cli/topic_create_step.d.ts +0 -84
- package/dist/setup/cli/topic_create_step.d.ts.map +0 -1
- package/dist/setup/cli/topic_create_step.js +0 -213
- package/dist/setup/cli/topic_create_step.js.map +0 -1
- package/dist/system-export.d.ts +0 -65
- package/dist/system-export.d.ts.map +0 -1
- package/dist/system-export.js +0 -194
- package/dist/system-export.js.map +0 -1
- package/dist/utterance/classifier.d.ts +0 -53
- package/dist/utterance/classifier.d.ts.map +0 -1
- package/dist/utterance/classifier.js +0 -184
- package/dist/utterance/classifier.js.map +0 -1
- package/dist/utterance/classifier.test.js +0 -147
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Per-project chat routing (v0.7.1 Phase C).
|
|
3
|
-
*
|
|
4
|
-
* Note on src.legacy/ placement (TG.1 (f) / WAB.2): the "legacy" label is a
|
|
5
|
-
* pre-0.5.x architecture marker, NOT a "dead code" marker. The chat-daemon
|
|
6
|
-
* compiled from `src.legacy/chat/` is the production runtime for inbound
|
|
7
|
-
* Telegram + outbound chat_send. This file is actively maintained — see
|
|
8
|
-
* `inbound_dm_user_ids` (v0.5.94 / WAB.2 Part A) below.
|
|
9
|
-
*
|
|
10
|
-
* Schema on disk: `~/.opensquid/projects/<uuid>/chat-routing.json`
|
|
11
|
-
*
|
|
12
|
-
* ```jsonc
|
|
13
|
-
* {
|
|
14
|
-
* "telegram": {
|
|
15
|
-
* "report_channel": "telegram:-1001234567890", // outbound default
|
|
16
|
-
* "report_topic_id": 15, // forum-topic id for outbound
|
|
17
|
-
* "inbound_chat_ids": ["-1001234567890"], // accepts inbound from these chats
|
|
18
|
-
* "inbound_topic_ids": [15], // strict whitelist when set
|
|
19
|
-
* "inbound_dm_user_ids": ["8075471258"] // v0.5.94 — DM allowlist
|
|
20
|
-
* },
|
|
21
|
-
* "discord": {
|
|
22
|
-
* "report_channel": "discord:1234567890",
|
|
23
|
-
* "inbound_channel_ids": ["1234567890"]
|
|
24
|
-
* },
|
|
25
|
-
* "slack": {
|
|
26
|
-
* "report_channel": "slack:C012345",
|
|
27
|
-
* "inbound_channel_ids": ["C012345"]
|
|
28
|
-
* }
|
|
29
|
-
* }
|
|
30
|
-
* ```
|
|
31
|
-
*
|
|
32
|
-
* Routing rules:
|
|
33
|
-
* - **Outbound** (agent → chat): the MCP tool picks `report_channel`
|
|
34
|
-
* from the active project's routing config. (Phase E wires the MCP
|
|
35
|
-
* tools; this module just exposes the lookup.)
|
|
36
|
-
* - **Inbound** (chat → agent): the daemon's gateway.onMessage handler
|
|
37
|
-
* constructs a routing key (DM key, topic key, or chat-only key per
|
|
38
|
-
* TG.1 policy decision (a)+(d)) and looks it up in the index this
|
|
39
|
-
* module builds. Match → JSONL append to that project's inbox.
|
|
40
|
-
* No match → JSONL append to the orphan inbox.
|
|
41
|
-
*
|
|
42
|
-
* UUID is the stable primary key because the project's human-friendly
|
|
43
|
-
* `id` can be renamed via `opensquid project init` without rewriting
|
|
44
|
-
* routing files.
|
|
45
|
-
*/
|
|
46
|
-
export interface TelegramRouting {
|
|
47
|
-
report_channel?: string;
|
|
48
|
-
/**
|
|
49
|
-
* v0.7.2 — forum-topic id within the supergroup (`report_channel`)
|
|
50
|
-
* that outbound reports for this project should post to. When set,
|
|
51
|
-
* outbound `chat_send` via `project:telegram` includes the
|
|
52
|
-
* `message_thread_id` so the message lands in the right topic.
|
|
53
|
-
*/
|
|
54
|
-
report_topic_id?: number;
|
|
55
|
-
inbound_chat_ids?: string[];
|
|
56
|
-
/**
|
|
57
|
-
* v0.7.2 — when set, ONLY inbound messages with one of these
|
|
58
|
-
* `message_thread_id` values route to this project. Empty/unset means
|
|
59
|
-
* accept any topic (legacy v0.7.1 behavior — accepts all messages
|
|
60
|
-
* from the listed `inbound_chat_ids`).
|
|
61
|
-
*/
|
|
62
|
-
inbound_topic_ids?: number[];
|
|
63
|
-
/**
|
|
64
|
-
* v0.5.94 (WAB.2 Part A / TG.1 decision (a)) — allowlist of user IDs
|
|
65
|
-
* whose Telegram DMs route to this project. A "DM" is detected when the
|
|
66
|
-
* inbound `chat.id === from.id` (Telegram's canonical private-chat
|
|
67
|
-
* shape). On match, the routing key is `telegram:dm:<user_id>`. Group
|
|
68
|
-
* messages from the same user use `inbound_chat_ids` + topic semantics
|
|
69
|
-
* — this field does NOT shadow group routing.
|
|
70
|
-
*
|
|
71
|
-
* Schema is additive: existing chat-routing.json files without this
|
|
72
|
-
* field continue to load and route correctly (DM routing simply
|
|
73
|
-
* disabled for that project).
|
|
74
|
-
*/
|
|
75
|
-
inbound_dm_user_ids?: string[];
|
|
76
|
-
}
|
|
77
|
-
export interface DiscordRouting {
|
|
78
|
-
report_channel?: string;
|
|
79
|
-
inbound_channel_ids?: string[];
|
|
80
|
-
}
|
|
81
|
-
export interface SlackRouting {
|
|
82
|
-
report_channel?: string;
|
|
83
|
-
inbound_channel_ids?: string[];
|
|
84
|
-
}
|
|
85
|
-
export interface ProjectChatRouting {
|
|
86
|
-
telegram?: TelegramRouting;
|
|
87
|
-
discord?: DiscordRouting;
|
|
88
|
-
slack?: SlackRouting;
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* In-memory index built from all per-project routing files. Used by
|
|
92
|
-
* the daemon's inbound handler to decide which project's inbox an
|
|
93
|
-
* incoming message belongs to.
|
|
94
|
-
*
|
|
95
|
-
* Key shape: `<platform>:<native_chat_id>` — same shape as ChannelId
|
|
96
|
-
* so the daemon can index directly off the parsed channel field of
|
|
97
|
-
* an inbound ChatMessage.
|
|
98
|
-
*/
|
|
99
|
-
export type RoutingIndex = Map<string, string>;
|
|
100
|
-
export declare function projectsRootPath(dataRoot?: string): string;
|
|
101
|
-
export declare function projectChatRoutingPath(projectUuid: string, dataRoot?: string): string;
|
|
102
|
-
export declare function projectInboxDir(projectUuid: string, dataRoot?: string): string;
|
|
103
|
-
export declare function orphanInboxDir(dataRoot?: string): string;
|
|
104
|
-
/**
|
|
105
|
-
* Read a single project's routing config. Returns null if the file
|
|
106
|
-
* doesn't exist or fails to parse — both are non-fatal (the project
|
|
107
|
-
* just doesn't have routing configured, so inbound for it goes to
|
|
108
|
-
* the orphan inbox).
|
|
109
|
-
*/
|
|
110
|
-
export declare function loadProjectChatRouting(projectUuid: string, dataRoot?: string): Promise<ProjectChatRouting | null>;
|
|
111
|
-
/**
|
|
112
|
-
* Scan `~/.opensquid/projects/*` and load every routing file that
|
|
113
|
-
* exists. Returns a Map keyed by project_uuid → routing config. Used
|
|
114
|
-
* by `buildRoutingIndex` to construct the lookup map.
|
|
115
|
-
*/
|
|
116
|
-
export declare function loadAllProjectChatRouting(dataRoot?: string): Promise<Map<string, ProjectChatRouting>>;
|
|
117
|
-
/**
|
|
118
|
-
* Build the inbound chat_id → project_uuid lookup map from a collection
|
|
119
|
-
* of per-project routing configs. Used at daemon startup AND whenever
|
|
120
|
-
* the file watcher fires on a routing change.
|
|
121
|
-
*
|
|
122
|
-
* Collision handling: if two projects claim the same inbound chat_id,
|
|
123
|
-
* the LATER one wins (Map.set overwrite). Surface a warning via the
|
|
124
|
-
* optional `onWarn` callback so the operator can fix it.
|
|
125
|
-
*/
|
|
126
|
-
export declare function buildRoutingIndex(configs: Map<string, ProjectChatRouting>, onWarn?: (message: string) => void): RoutingIndex;
|
|
127
|
-
/**
|
|
128
|
-
* Yield each inbound channel id (in `<platform>:<native_id>` shape) a
|
|
129
|
-
* routing config declares.
|
|
130
|
-
*/
|
|
131
|
-
export declare function collectInboundChannels(cfg: ProjectChatRouting): string[];
|
|
132
|
-
/**
|
|
133
|
-
* Persist a project's routing config. Creates the project directory if
|
|
134
|
-
* missing. Atomic-ish via write-then-rename so partial writes never
|
|
135
|
-
* leave a corrupt file.
|
|
136
|
-
*/
|
|
137
|
-
export declare function saveProjectChatRouting(projectUuid: string, routing: ProjectChatRouting, dataRoot?: string): Promise<{
|
|
138
|
-
path: string;
|
|
139
|
-
}>;
|
|
140
|
-
//# sourceMappingURL=routing.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../../../src.legacy/chat/daemon/routing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAWH,MAAM,WAAW,eAAe;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B;;;;;;;;;;;OAWG;IACH,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,cAAc;IAC7B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,YAAY;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,KAAK,CAAC,EAAE,YAAY,CAAC;CACtB;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,YAAY,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAoB,CAAC;AAMlE,wBAAgB,gBAAgB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAErF;AAED,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAE9E;AAED,wBAAgB,cAAc,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAExD;AAMD;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAC1C,WAAW,EAAE,MAAM,EACnB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAcpC;AAED;;;;GAIG;AACH,wBAAsB,yBAAyB,CAC7C,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAiB1C;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,kBAAkB,CAAC,EACxC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GACjC,YAAY,CAcd;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,kBAAkB,GAAG,MAAM,EAAE,CAgCxE;AAMD;;;;GAIG;AACH,wBAAsB,sBAAsB,CAC1C,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,kBAAkB,EAC3B,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAO3B"}
|
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Per-project chat routing (v0.7.1 Phase C).
|
|
3
|
-
*
|
|
4
|
-
* Note on src.legacy/ placement (TG.1 (f) / WAB.2): the "legacy" label is a
|
|
5
|
-
* pre-0.5.x architecture marker, NOT a "dead code" marker. The chat-daemon
|
|
6
|
-
* compiled from `src.legacy/chat/` is the production runtime for inbound
|
|
7
|
-
* Telegram + outbound chat_send. This file is actively maintained — see
|
|
8
|
-
* `inbound_dm_user_ids` (v0.5.94 / WAB.2 Part A) below.
|
|
9
|
-
*
|
|
10
|
-
* Schema on disk: `~/.opensquid/projects/<uuid>/chat-routing.json`
|
|
11
|
-
*
|
|
12
|
-
* ```jsonc
|
|
13
|
-
* {
|
|
14
|
-
* "telegram": {
|
|
15
|
-
* "report_channel": "telegram:-1001234567890", // outbound default
|
|
16
|
-
* "report_topic_id": 15, // forum-topic id for outbound
|
|
17
|
-
* "inbound_chat_ids": ["-1001234567890"], // accepts inbound from these chats
|
|
18
|
-
* "inbound_topic_ids": [15], // strict whitelist when set
|
|
19
|
-
* "inbound_dm_user_ids": ["8075471258"] // v0.5.94 — DM allowlist
|
|
20
|
-
* },
|
|
21
|
-
* "discord": {
|
|
22
|
-
* "report_channel": "discord:1234567890",
|
|
23
|
-
* "inbound_channel_ids": ["1234567890"]
|
|
24
|
-
* },
|
|
25
|
-
* "slack": {
|
|
26
|
-
* "report_channel": "slack:C012345",
|
|
27
|
-
* "inbound_channel_ids": ["C012345"]
|
|
28
|
-
* }
|
|
29
|
-
* }
|
|
30
|
-
* ```
|
|
31
|
-
*
|
|
32
|
-
* Routing rules:
|
|
33
|
-
* - **Outbound** (agent → chat): the MCP tool picks `report_channel`
|
|
34
|
-
* from the active project's routing config. (Phase E wires the MCP
|
|
35
|
-
* tools; this module just exposes the lookup.)
|
|
36
|
-
* - **Inbound** (chat → agent): the daemon's gateway.onMessage handler
|
|
37
|
-
* constructs a routing key (DM key, topic key, or chat-only key per
|
|
38
|
-
* TG.1 policy decision (a)+(d)) and looks it up in the index this
|
|
39
|
-
* module builds. Match → JSONL append to that project's inbox.
|
|
40
|
-
* No match → JSONL append to the orphan inbox.
|
|
41
|
-
*
|
|
42
|
-
* UUID is the stable primary key because the project's human-friendly
|
|
43
|
-
* `id` can be renamed via `opensquid project init` without rewriting
|
|
44
|
-
* routing files.
|
|
45
|
-
*/
|
|
46
|
-
import { promises as fs } from "node:fs";
|
|
47
|
-
import * as path from "node:path";
|
|
48
|
-
import { resolveDataRoot } from "../../codex/store.js";
|
|
49
|
-
// ---------------------------------------------------------------------
|
|
50
|
-
// Paths
|
|
51
|
-
// ---------------------------------------------------------------------
|
|
52
|
-
export function projectsRootPath(dataRoot) {
|
|
53
|
-
return path.join(resolveDataRoot(dataRoot), "projects");
|
|
54
|
-
}
|
|
55
|
-
export function projectChatRoutingPath(projectUuid, dataRoot) {
|
|
56
|
-
return path.join(projectsRootPath(dataRoot), projectUuid, "chat-routing.json");
|
|
57
|
-
}
|
|
58
|
-
export function projectInboxDir(projectUuid, dataRoot) {
|
|
59
|
-
return path.join(projectsRootPath(dataRoot), projectUuid, "inbox");
|
|
60
|
-
}
|
|
61
|
-
export function orphanInboxDir(dataRoot) {
|
|
62
|
-
return path.join(resolveDataRoot(dataRoot), "inbox", "orphan");
|
|
63
|
-
}
|
|
64
|
-
// ---------------------------------------------------------------------
|
|
65
|
-
// Load
|
|
66
|
-
// ---------------------------------------------------------------------
|
|
67
|
-
/**
|
|
68
|
-
* Read a single project's routing config. Returns null if the file
|
|
69
|
-
* doesn't exist or fails to parse — both are non-fatal (the project
|
|
70
|
-
* just doesn't have routing configured, so inbound for it goes to
|
|
71
|
-
* the orphan inbox).
|
|
72
|
-
*/
|
|
73
|
-
export async function loadProjectChatRouting(projectUuid, dataRoot) {
|
|
74
|
-
const p = projectChatRoutingPath(projectUuid, dataRoot);
|
|
75
|
-
try {
|
|
76
|
-
const raw = await fs.readFile(p, "utf8");
|
|
77
|
-
return JSON.parse(raw);
|
|
78
|
-
}
|
|
79
|
-
catch (err) {
|
|
80
|
-
if (err.code === "ENOENT")
|
|
81
|
-
return null;
|
|
82
|
-
// Malformed JSON: log on stderr (daemon log) and fall back to
|
|
83
|
-
// no-routing. Better than crashing the daemon over a bad file.
|
|
84
|
-
process.stderr.write(`[chat-routing] failed to parse ${p}: ${err instanceof Error ? err.message : err}\n`);
|
|
85
|
-
return null;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* Scan `~/.opensquid/projects/*` and load every routing file that
|
|
90
|
-
* exists. Returns a Map keyed by project_uuid → routing config. Used
|
|
91
|
-
* by `buildRoutingIndex` to construct the lookup map.
|
|
92
|
-
*/
|
|
93
|
-
export async function loadAllProjectChatRouting(dataRoot) {
|
|
94
|
-
const root = projectsRootPath(dataRoot);
|
|
95
|
-
const out = new Map();
|
|
96
|
-
let entries;
|
|
97
|
-
try {
|
|
98
|
-
entries = await fs.readdir(root);
|
|
99
|
-
}
|
|
100
|
-
catch (err) {
|
|
101
|
-
if (err.code === "ENOENT")
|
|
102
|
-
return out;
|
|
103
|
-
throw err;
|
|
104
|
-
}
|
|
105
|
-
for (const uuid of entries) {
|
|
106
|
-
// Skip hidden files; only consider directory-like entries.
|
|
107
|
-
if (uuid.startsWith("."))
|
|
108
|
-
continue;
|
|
109
|
-
const routing = await loadProjectChatRouting(uuid, dataRoot);
|
|
110
|
-
if (routing)
|
|
111
|
-
out.set(uuid, routing);
|
|
112
|
-
}
|
|
113
|
-
return out;
|
|
114
|
-
}
|
|
115
|
-
/**
|
|
116
|
-
* Build the inbound chat_id → project_uuid lookup map from a collection
|
|
117
|
-
* of per-project routing configs. Used at daemon startup AND whenever
|
|
118
|
-
* the file watcher fires on a routing change.
|
|
119
|
-
*
|
|
120
|
-
* Collision handling: if two projects claim the same inbound chat_id,
|
|
121
|
-
* the LATER one wins (Map.set overwrite). Surface a structured warning
|
|
122
|
-
* via the optional `onCollision` callback so the operator can fix it.
|
|
123
|
-
*/
|
|
124
|
-
export function buildRoutingIndex(configs, onCollision) {
|
|
125
|
-
const idx = new Map();
|
|
126
|
-
for (const [projectUuid, cfg] of configs) {
|
|
127
|
-
for (const channelKey of collectInboundChannels(cfg)) {
|
|
128
|
-
const existing = idx.get(channelKey);
|
|
129
|
-
if (existing && existing !== projectUuid && onCollision) {
|
|
130
|
-
onCollision({
|
|
131
|
-
channel_key: channelKey,
|
|
132
|
-
existing_uuid: existing,
|
|
133
|
-
newcomer_uuid: projectUuid,
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
idx.set(channelKey, projectUuid);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
return idx;
|
|
140
|
-
}
|
|
141
|
-
/**
|
|
142
|
-
* Yield each inbound channel id (in `<platform>:<native_id>` shape) a
|
|
143
|
-
* routing config declares.
|
|
144
|
-
*/
|
|
145
|
-
export function collectInboundChannels(cfg) {
|
|
146
|
-
const out = [];
|
|
147
|
-
if (cfg.telegram?.inbound_chat_ids) {
|
|
148
|
-
// v0.7.2: if inbound_topic_ids is set, emit a more-specific key per
|
|
149
|
-
// (chat_id, topic_id) tuple so the routing index can distinguish
|
|
150
|
-
// multiple projects sharing one supergroup. Otherwise emit the
|
|
151
|
-
// chat-only key (v0.7.1 behavior).
|
|
152
|
-
const topicIds = cfg.telegram.inbound_topic_ids;
|
|
153
|
-
for (const chatId of cfg.telegram.inbound_chat_ids) {
|
|
154
|
-
if (topicIds && topicIds.length > 0) {
|
|
155
|
-
for (const tid of topicIds)
|
|
156
|
-
out.push(`telegram:${chatId}:${tid}`);
|
|
157
|
-
}
|
|
158
|
-
else {
|
|
159
|
-
out.push(`telegram:${chatId}`);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
// v0.5.94 (WAB.2 Part A / TG.1 (a)): emit DM allowlist keys as
|
|
164
|
-
// `telegram:dm:<user_id>`. Worker's onMessage detects DMs (chat.id ===
|
|
165
|
-
// from.id) and looks up against this key. Separate key namespace from
|
|
166
|
-
// `telegram:<chat_id>` so a user whose user_id collides with a
|
|
167
|
-
// supergroup chat_id (unlikely but possible — both are integers) does
|
|
168
|
-
// not silently cross-route.
|
|
169
|
-
if (cfg.telegram?.inbound_dm_user_ids) {
|
|
170
|
-
for (const uid of cfg.telegram.inbound_dm_user_ids)
|
|
171
|
-
out.push(`telegram:dm:${uid}`);
|
|
172
|
-
}
|
|
173
|
-
if (cfg.discord?.inbound_channel_ids) {
|
|
174
|
-
for (const id of cfg.discord.inbound_channel_ids)
|
|
175
|
-
out.push(`discord:${id}`);
|
|
176
|
-
}
|
|
177
|
-
if (cfg.slack?.inbound_channel_ids) {
|
|
178
|
-
for (const id of cfg.slack.inbound_channel_ids)
|
|
179
|
-
out.push(`slack:${id}`);
|
|
180
|
-
}
|
|
181
|
-
return out;
|
|
182
|
-
}
|
|
183
|
-
// ---------------------------------------------------------------------
|
|
184
|
-
// Write
|
|
185
|
-
// ---------------------------------------------------------------------
|
|
186
|
-
/**
|
|
187
|
-
* Persist a project's routing config. Creates the project directory if
|
|
188
|
-
* missing. Atomic-ish via write-then-rename so partial writes never
|
|
189
|
-
* leave a corrupt file.
|
|
190
|
-
*/
|
|
191
|
-
export async function saveProjectChatRouting(projectUuid, routing, dataRoot) {
|
|
192
|
-
const filePath = projectChatRoutingPath(projectUuid, dataRoot);
|
|
193
|
-
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
194
|
-
const tmp = `${filePath}.tmp-${process.pid}-${Date.now()}`;
|
|
195
|
-
await fs.writeFile(tmp, `${JSON.stringify(routing, null, 2)}\n`, "utf8");
|
|
196
|
-
await fs.rename(tmp, filePath);
|
|
197
|
-
return { path: filePath };
|
|
198
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"routing.js","sourceRoot":"","sources":["../../../src.legacy/chat/daemon/routing.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,259 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* routing.ts unit tests (v0.7.1 Phase C).
|
|
3
|
-
*/
|
|
4
|
-
import { promises as fs } from "node:fs";
|
|
5
|
-
import * as os from "node:os";
|
|
6
|
-
import * as path from "node:path";
|
|
7
|
-
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
8
|
-
import { buildRoutingIndex, collectInboundChannels, loadAllProjectChatRouting, loadProjectChatRouting, projectChatRoutingPath, projectInboxDir, projectsRootPath, saveProjectChatRouting, } from "./routing.js";
|
|
9
|
-
let tmpRoot;
|
|
10
|
-
let prevHome;
|
|
11
|
-
beforeEach(async () => {
|
|
12
|
-
tmpRoot = await fs.mkdtemp(path.join(os.tmpdir(), "opensquid-routing-test-"));
|
|
13
|
-
prevHome = process.env.OPENSQUID_HOME;
|
|
14
|
-
process.env.OPENSQUID_HOME = tmpRoot;
|
|
15
|
-
});
|
|
16
|
-
afterEach(async () => {
|
|
17
|
-
if (prevHome === undefined)
|
|
18
|
-
delete process.env.OPENSQUID_HOME;
|
|
19
|
-
else
|
|
20
|
-
process.env.OPENSQUID_HOME = prevHome;
|
|
21
|
-
await fs.rm(tmpRoot, { recursive: true, force: true });
|
|
22
|
-
});
|
|
23
|
-
describe("path derivation", () => {
|
|
24
|
-
it("projectsRootPath = <dataRoot>/projects", () => {
|
|
25
|
-
expect(projectsRootPath(tmpRoot)).toBe(path.join(tmpRoot, "projects"));
|
|
26
|
-
});
|
|
27
|
-
it("projectChatRoutingPath = <dataRoot>/projects/<uuid>/chat-routing.json", () => {
|
|
28
|
-
expect(projectChatRoutingPath("abc-uuid", tmpRoot)).toBe(path.join(tmpRoot, "projects", "abc-uuid", "chat-routing.json"));
|
|
29
|
-
});
|
|
30
|
-
it("projectInboxDir = <dataRoot>/projects/<uuid>/inbox", () => {
|
|
31
|
-
expect(projectInboxDir("abc-uuid", tmpRoot)).toBe(path.join(tmpRoot, "projects", "abc-uuid", "inbox"));
|
|
32
|
-
});
|
|
33
|
-
});
|
|
34
|
-
describe("loadProjectChatRouting", () => {
|
|
35
|
-
it("returns null when the routing file is missing", async () => {
|
|
36
|
-
const r = await loadProjectChatRouting("missing-uuid", tmpRoot);
|
|
37
|
-
expect(r).toBeNull();
|
|
38
|
-
});
|
|
39
|
-
it("loads a valid routing file", async () => {
|
|
40
|
-
const uuid = "test-uuid";
|
|
41
|
-
await fs.mkdir(path.join(tmpRoot, "projects", uuid), { recursive: true });
|
|
42
|
-
await fs.writeFile(path.join(tmpRoot, "projects", uuid, "chat-routing.json"), JSON.stringify({
|
|
43
|
-
telegram: { report_channel: "telegram:-100", inbound_chat_ids: ["-100", "-200"] },
|
|
44
|
-
}));
|
|
45
|
-
const r = await loadProjectChatRouting(uuid, tmpRoot);
|
|
46
|
-
expect(r).not.toBeNull();
|
|
47
|
-
expect(r?.telegram?.report_channel).toBe("telegram:-100");
|
|
48
|
-
expect(r?.telegram?.inbound_chat_ids).toEqual(["-100", "-200"]);
|
|
49
|
-
});
|
|
50
|
-
it("returns null on malformed JSON (does not throw)", async () => {
|
|
51
|
-
const uuid = "bad-uuid";
|
|
52
|
-
await fs.mkdir(path.join(tmpRoot, "projects", uuid), { recursive: true });
|
|
53
|
-
await fs.writeFile(path.join(tmpRoot, "projects", uuid, "chat-routing.json"), "{ not json");
|
|
54
|
-
const r = await loadProjectChatRouting(uuid, tmpRoot);
|
|
55
|
-
expect(r).toBeNull();
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
describe("loadAllProjectChatRouting", () => {
|
|
59
|
-
it("returns empty map when projects root doesn't exist", async () => {
|
|
60
|
-
const r = await loadAllProjectChatRouting(tmpRoot);
|
|
61
|
-
expect(r.size).toBe(0);
|
|
62
|
-
});
|
|
63
|
-
it("collects all valid routing files, skipping hidden entries", async () => {
|
|
64
|
-
const projectsDir = path.join(tmpRoot, "projects");
|
|
65
|
-
await fs.mkdir(path.join(projectsDir, "uuid-a"), { recursive: true });
|
|
66
|
-
await fs.mkdir(path.join(projectsDir, "uuid-b"), { recursive: true });
|
|
67
|
-
await fs.mkdir(path.join(projectsDir, ".hidden"), { recursive: true });
|
|
68
|
-
await fs.writeFile(path.join(projectsDir, "uuid-a", "chat-routing.json"), JSON.stringify({ telegram: { inbound_chat_ids: ["1"] } }));
|
|
69
|
-
await fs.writeFile(path.join(projectsDir, "uuid-b", "chat-routing.json"), JSON.stringify({ discord: { inbound_channel_ids: ["2"] } }));
|
|
70
|
-
await fs.writeFile(path.join(projectsDir, ".hidden", "chat-routing.json"), JSON.stringify({ slack: { inbound_channel_ids: ["3"] } }));
|
|
71
|
-
const all = await loadAllProjectChatRouting(tmpRoot);
|
|
72
|
-
expect(all.size).toBe(2);
|
|
73
|
-
expect(all.has("uuid-a")).toBe(true);
|
|
74
|
-
expect(all.has("uuid-b")).toBe(true);
|
|
75
|
-
expect(all.has(".hidden")).toBe(false);
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
describe("collectInboundChannels", () => {
|
|
79
|
-
it("returns empty list for empty config", () => {
|
|
80
|
-
expect(collectInboundChannels({})).toEqual([]);
|
|
81
|
-
});
|
|
82
|
-
it("formats telegram ids as telegram:<id>", () => {
|
|
83
|
-
const ch = collectInboundChannels({ telegram: { inbound_chat_ids: ["100", "200"] } });
|
|
84
|
-
expect(ch).toEqual(["telegram:100", "telegram:200"]);
|
|
85
|
-
});
|
|
86
|
-
it("collects across all platforms", () => {
|
|
87
|
-
const ch = collectInboundChannels({
|
|
88
|
-
telegram: { inbound_chat_ids: ["t1"] },
|
|
89
|
-
discord: { inbound_channel_ids: ["d1"] },
|
|
90
|
-
slack: { inbound_channel_ids: ["s1"] },
|
|
91
|
-
});
|
|
92
|
-
expect(ch).toEqual(["telegram:t1", "discord:d1", "slack:s1"]);
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
|
-
// v0.5.94 (WAB.2 Part A / TG.1 (a)) — DM allowlist key emission.
|
|
96
|
-
describe("collectInboundChannels — v0.5.94 DM allowlist", () => {
|
|
97
|
-
it("emits DM keys as telegram:dm:<user_id> when inbound_dm_user_ids is set", () => {
|
|
98
|
-
const ch = collectInboundChannels({
|
|
99
|
-
telegram: { inbound_dm_user_ids: ["123"] },
|
|
100
|
-
});
|
|
101
|
-
expect(ch).toEqual(["telegram:dm:123"]);
|
|
102
|
-
});
|
|
103
|
-
it("supports multiple DM user_ids", () => {
|
|
104
|
-
const ch = collectInboundChannels({
|
|
105
|
-
telegram: { inbound_dm_user_ids: ["123", "456"] },
|
|
106
|
-
});
|
|
107
|
-
expect(ch).toEqual(["telegram:dm:123", "telegram:dm:456"]);
|
|
108
|
-
});
|
|
109
|
-
it("emits both group + topic + DM keys when all are configured", () => {
|
|
110
|
-
const ch = collectInboundChannels({
|
|
111
|
-
telegram: {
|
|
112
|
-
inbound_chat_ids: ["-100super"],
|
|
113
|
-
inbound_topic_ids: [15],
|
|
114
|
-
inbound_dm_user_ids: ["8075471258"],
|
|
115
|
-
},
|
|
116
|
-
});
|
|
117
|
-
expect(ch).toEqual(["telegram:-100super:15", "telegram:dm:8075471258"]);
|
|
118
|
-
});
|
|
119
|
-
it("schema backwards-compat: config without inbound_dm_user_ids still works", () => {
|
|
120
|
-
const ch = collectInboundChannels({
|
|
121
|
-
telegram: { inbound_chat_ids: ["-100"] },
|
|
122
|
-
});
|
|
123
|
-
expect(ch).toEqual(["telegram:-100"]);
|
|
124
|
-
});
|
|
125
|
-
it("empty inbound_dm_user_ids list emits no DM keys", () => {
|
|
126
|
-
const ch = collectInboundChannels({
|
|
127
|
-
telegram: { inbound_dm_user_ids: [] },
|
|
128
|
-
});
|
|
129
|
-
expect(ch).toEqual([]);
|
|
130
|
-
});
|
|
131
|
-
});
|
|
132
|
-
describe("buildRoutingIndex — v0.5.94 DM key indexing", () => {
|
|
133
|
-
it("indexes DM keys so worker can look up by telegram:dm:<user_id>", () => {
|
|
134
|
-
const cfgs = new Map([
|
|
135
|
-
["uuid-dm", { telegram: { inbound_dm_user_ids: ["8075471258"] } }],
|
|
136
|
-
]);
|
|
137
|
-
const idx = buildRoutingIndex(cfgs);
|
|
138
|
-
expect(idx.get("telegram:dm:8075471258")).toBe("uuid-dm");
|
|
139
|
-
expect(idx.size).toBe(1);
|
|
140
|
-
});
|
|
141
|
-
it("two projects can each have their own DM allowlist (no collision)", () => {
|
|
142
|
-
const cfgs = new Map([
|
|
143
|
-
["uuid-a", { telegram: { inbound_dm_user_ids: ["111"] } }],
|
|
144
|
-
["uuid-b", { telegram: { inbound_dm_user_ids: ["222"] } }],
|
|
145
|
-
]);
|
|
146
|
-
const idx = buildRoutingIndex(cfgs);
|
|
147
|
-
expect(idx.get("telegram:dm:111")).toBe("uuid-a");
|
|
148
|
-
expect(idx.get("telegram:dm:222")).toBe("uuid-b");
|
|
149
|
-
expect(idx.size).toBe(2);
|
|
150
|
-
});
|
|
151
|
-
it("same DM user across two projects: collision warned, last wins (consistent with existing behavior)", () => {
|
|
152
|
-
const cfgs = new Map([
|
|
153
|
-
["uuid-a", { telegram: { inbound_dm_user_ids: ["999"] } }],
|
|
154
|
-
["uuid-b", { telegram: { inbound_dm_user_ids: ["999"] } }],
|
|
155
|
-
]);
|
|
156
|
-
const warnings = [];
|
|
157
|
-
const idx = buildRoutingIndex(cfgs, (m) => warnings.push(m));
|
|
158
|
-
expect(idx.get("telegram:dm:999")).toBe("uuid-b");
|
|
159
|
-
expect(warnings.length).toBe(1);
|
|
160
|
-
expect(warnings[0]).toMatch(/collision.*telegram:dm:999/);
|
|
161
|
-
});
|
|
162
|
-
it("group + DM in same project: both keys index to same uuid", () => {
|
|
163
|
-
const cfgs = new Map([
|
|
164
|
-
[
|
|
165
|
-
"uuid-x",
|
|
166
|
-
{
|
|
167
|
-
telegram: {
|
|
168
|
-
inbound_chat_ids: ["-100"],
|
|
169
|
-
inbound_topic_ids: [15],
|
|
170
|
-
inbound_dm_user_ids: ["8075471258"],
|
|
171
|
-
},
|
|
172
|
-
},
|
|
173
|
-
],
|
|
174
|
-
]);
|
|
175
|
-
const idx = buildRoutingIndex(cfgs);
|
|
176
|
-
expect(idx.get("telegram:-100:15")).toBe("uuid-x");
|
|
177
|
-
expect(idx.get("telegram:dm:8075471258")).toBe("uuid-x");
|
|
178
|
-
expect(idx.get("telegram:-100")).toBeUndefined(); // strict topic whitelist preserved
|
|
179
|
-
expect(idx.size).toBe(2);
|
|
180
|
-
});
|
|
181
|
-
});
|
|
182
|
-
describe("buildRoutingIndex — v0.7.2 topic-aware keys", () => {
|
|
183
|
-
it("emits topic-specific keys when inbound_topic_ids is set", () => {
|
|
184
|
-
const cfgs = new Map([
|
|
185
|
-
[
|
|
186
|
-
"uuid-a",
|
|
187
|
-
{
|
|
188
|
-
telegram: {
|
|
189
|
-
inbound_chat_ids: ["-1001234567890"],
|
|
190
|
-
inbound_topic_ids: [5, 12],
|
|
191
|
-
},
|
|
192
|
-
},
|
|
193
|
-
],
|
|
194
|
-
]);
|
|
195
|
-
const idx = buildRoutingIndex(cfgs);
|
|
196
|
-
expect(idx.get("telegram:-1001234567890:5")).toBe("uuid-a");
|
|
197
|
-
expect(idx.get("telegram:-1001234567890:12")).toBe("uuid-a");
|
|
198
|
-
// Chat-only key NOT registered when topic_ids are set — we want
|
|
199
|
-
// routing to be topic-strict.
|
|
200
|
-
expect(idx.get("telegram:-1001234567890")).toBeUndefined();
|
|
201
|
-
});
|
|
202
|
-
it("two projects sharing one supergroup but different topics get distinct routing", () => {
|
|
203
|
-
const cfgs = new Map([
|
|
204
|
-
["uuid-a", { telegram: { inbound_chat_ids: ["-100common"], inbound_topic_ids: [5] } }],
|
|
205
|
-
["uuid-b", { telegram: { inbound_chat_ids: ["-100common"], inbound_topic_ids: [10] } }],
|
|
206
|
-
]);
|
|
207
|
-
const idx = buildRoutingIndex(cfgs);
|
|
208
|
-
expect(idx.get("telegram:-100common:5")).toBe("uuid-a");
|
|
209
|
-
expect(idx.get("telegram:-100common:10")).toBe("uuid-b");
|
|
210
|
-
expect(idx.size).toBe(2);
|
|
211
|
-
});
|
|
212
|
-
it("falls back to chat-only key when inbound_topic_ids is empty/unset (legacy v0.7.1)", () => {
|
|
213
|
-
const cfgs = new Map([["uuid-legacy", { telegram: { inbound_chat_ids: ["-100legacy"] } }]]);
|
|
214
|
-
const idx = buildRoutingIndex(cfgs);
|
|
215
|
-
expect(idx.get("telegram:-100legacy")).toBe("uuid-legacy");
|
|
216
|
-
expect(idx.size).toBe(1);
|
|
217
|
-
});
|
|
218
|
-
});
|
|
219
|
-
describe("buildRoutingIndex", () => {
|
|
220
|
-
it("builds a chat_id → uuid map", () => {
|
|
221
|
-
const cfgs = new Map([
|
|
222
|
-
["uuid-a", { telegram: { inbound_chat_ids: ["100", "200"] } }],
|
|
223
|
-
["uuid-b", { discord: { inbound_channel_ids: ["d1"] } }],
|
|
224
|
-
]);
|
|
225
|
-
const idx = buildRoutingIndex(cfgs);
|
|
226
|
-
expect(idx.get("telegram:100")).toBe("uuid-a");
|
|
227
|
-
expect(idx.get("telegram:200")).toBe("uuid-a");
|
|
228
|
-
expect(idx.get("discord:d1")).toBe("uuid-b");
|
|
229
|
-
expect(idx.size).toBe(3);
|
|
230
|
-
});
|
|
231
|
-
it("calls onWarn on collision and lets the later project win", () => {
|
|
232
|
-
const cfgs = new Map([
|
|
233
|
-
["uuid-a", { telegram: { inbound_chat_ids: ["100"] } }],
|
|
234
|
-
["uuid-b", { telegram: { inbound_chat_ids: ["100"] } }],
|
|
235
|
-
]);
|
|
236
|
-
const warnings = [];
|
|
237
|
-
const idx = buildRoutingIndex(cfgs, (m) => warnings.push(m));
|
|
238
|
-
// Map iteration order on insertion → uuid-b wins.
|
|
239
|
-
expect(idx.get("telegram:100")).toBe("uuid-b");
|
|
240
|
-
expect(warnings.length).toBe(1);
|
|
241
|
-
expect(warnings[0]).toMatch(/collision.*telegram:100/);
|
|
242
|
-
});
|
|
243
|
-
});
|
|
244
|
-
describe("saveProjectChatRouting", () => {
|
|
245
|
-
it("writes a routing file atomically, creating the project dir if missing", async () => {
|
|
246
|
-
const uuid = "save-test";
|
|
247
|
-
const res = await saveProjectChatRouting(uuid, { telegram: { report_channel: "telegram:abc", inbound_chat_ids: ["abc"] } }, tmpRoot);
|
|
248
|
-
expect(res.path).toBe(path.join(tmpRoot, "projects", uuid, "chat-routing.json"));
|
|
249
|
-
const back = await loadProjectChatRouting(uuid, tmpRoot);
|
|
250
|
-
expect(back?.telegram?.report_channel).toBe("telegram:abc");
|
|
251
|
-
});
|
|
252
|
-
it("overwrites an existing routing file (rename-over)", async () => {
|
|
253
|
-
const uuid = "overwrite-test";
|
|
254
|
-
await saveProjectChatRouting(uuid, { telegram: { report_channel: "telegram:v1" } }, tmpRoot);
|
|
255
|
-
await saveProjectChatRouting(uuid, { telegram: { report_channel: "telegram:v2" } }, tmpRoot);
|
|
256
|
-
const back = await loadProjectChatRouting(uuid, tmpRoot);
|
|
257
|
-
expect(back?.telegram?.report_channel).toBe("telegram:v2");
|
|
258
|
-
});
|
|
259
|
-
});
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* chat-daemon RPC client (v0.7.1 Phase B).
|
|
3
|
-
*
|
|
4
|
-
* Used by the MCP server when it wants to send a chat message but
|
|
5
|
-
* doesn't want to start its own long-poll adapter (which would
|
|
6
|
-
* collide with any other MCP server holding the same bot token).
|
|
7
|
-
*
|
|
8
|
-
* Behavior:
|
|
9
|
-
* - One short-lived connection per request — keeps the implementation
|
|
10
|
-
* trivial and avoids reconnect/backoff logic. Performance is fine
|
|
11
|
-
* for v0.7.1's expected traffic (a handful of agent reports per
|
|
12
|
-
* project session).
|
|
13
|
-
* - Returns a typed result on success.
|
|
14
|
-
* - Throws `DaemonUnreachableError` when the socket can't be reached
|
|
15
|
-
* so callers can fall back to in-process send.
|
|
16
|
-
* - Throws `DaemonRpcError` on JSON-RPC error responses.
|
|
17
|
-
*/
|
|
18
|
-
import { type CreateTopicParams, type CreateTopicResult, type ListChannelsResult, type PingResult, type SendParams, type SendResult } from "./protocol.js";
|
|
19
|
-
export declare class DaemonUnreachableError extends Error {
|
|
20
|
-
constructor(message: string);
|
|
21
|
-
}
|
|
22
|
-
export declare class DaemonRpcError extends Error {
|
|
23
|
-
readonly code: number;
|
|
24
|
-
constructor(message: string, code: number);
|
|
25
|
-
}
|
|
26
|
-
export interface RpcClientOptions {
|
|
27
|
-
dataRoot?: string;
|
|
28
|
-
/** Connect timeout in ms. Default 1500 — local socket, fast. */
|
|
29
|
-
connectTimeoutMs?: number;
|
|
30
|
-
/** Request timeout in ms (waiting for response). Default 5000. */
|
|
31
|
-
requestTimeoutMs?: number;
|
|
32
|
-
}
|
|
33
|
-
export declare class DaemonClient {
|
|
34
|
-
private readonly address;
|
|
35
|
-
private readonly connectTimeoutMs;
|
|
36
|
-
private readonly requestTimeoutMs;
|
|
37
|
-
constructor(opts?: RpcClientOptions);
|
|
38
|
-
ping(): Promise<PingResult>;
|
|
39
|
-
listChannels(): Promise<ListChannelsResult>;
|
|
40
|
-
send(params: SendParams): Promise<SendResult>;
|
|
41
|
-
createTopic(params: CreateTopicParams): Promise<CreateTopicResult>;
|
|
42
|
-
call<R>(method: string, params: unknown): Promise<R>;
|
|
43
|
-
private openSocket;
|
|
44
|
-
}
|
|
45
|
-
//# sourceMappingURL=rpc-client.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"rpc-client.d.ts","sourceRoot":"","sources":["../../../src.legacy/chat/daemon/rpc-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EAGtB,KAAK,kBAAkB,EACvB,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,UAAU,EAEhB,MAAM,eAAe,CAAC;AAEvB,qBAAa,sBAAuB,SAAQ,KAAK;gBACnC,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,cAAe,SAAQ,KAAK;IAGrC,QAAQ,CAAC,IAAI,EAAE,MAAM;gBADrB,OAAO,EAAE,MAAM,EACN,IAAI,EAAE,MAAM;CAKxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gEAAgE;IAChE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,kEAAkE;IAClE,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAID,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;gBAE9B,IAAI,GAAE,gBAAqB;IAMvC,IAAI,IAAI,OAAO,CAAC,UAAU,CAAC;IAI3B,YAAY,IAAI,OAAO,CAAC,kBAAkB,CAAC;IAI3C,IAAI,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IAI7C,WAAW,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAI5D,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAiB1D,OAAO,CAAC,UAAU;CAwBnB"}
|