@atheon-inc/openclaw-plugin 0.0.0 → 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.
- package/README.md +180 -0
- package/dist/hooks/llm.js +4 -0
- package/dist/hooks/llm.js.map +7 -0
- package/dist/hooks/subagent.js +2 -0
- package/dist/hooks/subagent.js.map +7 -0
- package/dist/hooks/tool.js +2 -0
- package/dist/hooks/tool.js.map +7 -0
- package/dist/index.js +2 -13
- package/dist/index.js.map +7 -0
- package/dist/payload_sanitizer.js +6 -0
- package/dist/payload_sanitizer.js.map +7 -0
- package/dist/service.js +2 -0
- package/dist/service.js.map +7 -0
- package/dist/session_registry.js +2 -0
- package/dist/session_registry.js.map +7 -0
- package/dist/utils.js +2 -0
- package/dist/utils.js.map +7 -0
- package/openclaw.plugin.json +24 -0
- package/package.json +50 -49
- /package/{License.md → LICENSE} +0 -0
package/README.md
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# Atheon OpenClaw Plugin
|
|
2
|
+
|
|
3
|
+
## Why This Plugin
|
|
4
|
+
|
|
5
|
+
[Atheon](https://atheon-inc.com) is a purpose-build modern analytics platform for LLM agents.
|
|
6
|
+
|
|
7
|
+
`@atheon-inc/openclaw-plugin` adds native Atheon analytics capabilities to OpenClaw runs:
|
|
8
|
+
|
|
9
|
+
- LLM request and response traces with token usage
|
|
10
|
+
- Sub-agent lifecycle nested under their parent trace
|
|
11
|
+
- Tool call traces with latency, inputs, and errors
|
|
12
|
+
- Automatic cleanup of abandoned or timed-out interactions
|
|
13
|
+
- Payload sanitization — internal OpenClaw metadata is stripped before leaving the gateway
|
|
14
|
+
|
|
15
|
+
The plugin runs inside the OpenClaw Gateway process. If your gateway is remote, install and configure the plugin on that host.
|
|
16
|
+
|
|
17
|
+
## Install and first run
|
|
18
|
+
|
|
19
|
+
Prerequisites:
|
|
20
|
+
|
|
21
|
+
- OpenClaw `>=2026.3.2`
|
|
22
|
+
- Node.js `>=22.12.0`
|
|
23
|
+
- npm `>=10`
|
|
24
|
+
|
|
25
|
+
### 1. Install the plugin in OpenClaw
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
openclaw plugins install clawhub:@atheon-inc/openclaw-plugin
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
For older versions of OpenClaw `<2026.3.23` install directly from npm:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
openclaw plugins install @atheon-inc/openclaw-plugin
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
If the gateway is already running, restart it after install.
|
|
38
|
+
|
|
39
|
+
### 2. Set your API key
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
export ATHEON_API_KEY=arc_...
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Or pass it via the plugin config block — see [Configuration](#configuration) below.
|
|
46
|
+
|
|
47
|
+
### 3. Check effective settings
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
openclaw plugins status atheon-openclaw
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 4. Send a test message
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
openclaw gateway run
|
|
57
|
+
openclaw message send "hello from openclaw"
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Then confirm traces appear in your Atheon Codex project.
|
|
61
|
+
|
|
62
|
+
## Configuration
|
|
63
|
+
|
|
64
|
+
### Recommended config shape
|
|
65
|
+
|
|
66
|
+
```json
|
|
67
|
+
{
|
|
68
|
+
"plugins": {
|
|
69
|
+
"entries": {
|
|
70
|
+
"atheon-openclaw": {
|
|
71
|
+
"enabled": true,
|
|
72
|
+
"config": {
|
|
73
|
+
"apiKey": "arc_...",
|
|
74
|
+
"uploadSize": 100,
|
|
75
|
+
"uploadInterval": 5000
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Config reference
|
|
84
|
+
|
|
85
|
+
| Option | Environment variable | Type | Required | Description |
|
|
86
|
+
|------------------|----------------------|----------|-----------|-----------------------------------------------------------------|
|
|
87
|
+
| `apiKey` | `ATHEON_API_KEY` | `string` | ✅ | Atheon Codex API key. |
|
|
88
|
+
| `uploadSize` | — | `number` | — | Number of spans to batch before uploading. |
|
|
89
|
+
| `uploadInterval` | — | `number` | — | Maximum milliseconds between uploads, regardless of batch size. |
|
|
90
|
+
|
|
91
|
+
`apiKey` is required. The plugin will fail to start at startup if it is absent from both the config block and `ATHEON_API_KEY`.
|
|
92
|
+
|
|
93
|
+
### Plugin trust allowlist
|
|
94
|
+
|
|
95
|
+
OpenClaw warns when `plugins.allow` is empty and a community plugin is discovered. Pin trusted plugins explicitly:
|
|
96
|
+
|
|
97
|
+
```json
|
|
98
|
+
{
|
|
99
|
+
"plugins": {
|
|
100
|
+
"allow": ["atheon-openclaw"]
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Event mapping
|
|
106
|
+
|
|
107
|
+
| OpenClaw event | Atheon Codex entity | Notes |
|
|
108
|
+
|---------------------|------------------------------|------------------------------------------------------------------------|
|
|
109
|
+
| `llm_input` | root interaction start | Opens a new interaction; updates provider/model on an existing child. |
|
|
110
|
+
| `llm_output` | root interaction finish | Records output text and token usage; deferred if tools are in flight. |
|
|
111
|
+
| `before_tool_call` | tool span start | Captures tool name and start timestamp. |
|
|
112
|
+
| `after_tool_call` | tool span finish | Records latency and error, then closes the span. |
|
|
113
|
+
| `subagent_spawning` | child interaction start | Spawns a child interaction nested under the parent. |
|
|
114
|
+
| `subagent_spawned` | child interaction update | Enriches child with run ID and thread metadata. |
|
|
115
|
+
| `subagent_ended` | child interaction finish | Finalizes child with outcome and error. |
|
|
116
|
+
| `session_end` | root interaction force-close | Closes any interaction still open when the session ends. |
|
|
117
|
+
|
|
118
|
+
## Trace structure
|
|
119
|
+
|
|
120
|
+
Each session produces one root interaction. Subagents produce child interactions nested underneath it. Tool calls are recorded as spans on whichever interaction made the call.
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
Session
|
|
124
|
+
└── Root interaction (llm_input → llm_output)
|
|
125
|
+
├── Tool span (before_tool_call → after_tool_call)
|
|
126
|
+
├── Tool span
|
|
127
|
+
└── Child interaction (subagent_spawning → subagent_ended)
|
|
128
|
+
├── Tool span
|
|
129
|
+
└── Tool span
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
If a session ends while tool calls or subagents are still in flight, the plugin records them as abandoned with an error message and closes the tree cleanly.
|
|
133
|
+
|
|
134
|
+
A background reaper runs every **5 minutes** and closes interactions idle for more than **1 hour**, preventing unbounded memory growth from sessions that end without a clean `session_end` event.
|
|
135
|
+
|
|
136
|
+
## Supported providers
|
|
137
|
+
|
|
138
|
+
Provider strings from the gateway are normalized before being sent to Atheon Codex:
|
|
139
|
+
|
|
140
|
+
| Raw string (case-insensitive, substring match) | Normalized |
|
|
141
|
+
|------------------------------------------------|------------------|
|
|
142
|
+
| `anthropic` | `anthropic` |
|
|
143
|
+
| `openai` | `openai` |
|
|
144
|
+
| `google`, `gemini` | `google` |
|
|
145
|
+
| `mistral` | `mistral` |
|
|
146
|
+
| `cohere` | `cohere` |
|
|
147
|
+
| anything else | lowercased as-is |
|
|
148
|
+
|
|
149
|
+
## Known limitations
|
|
150
|
+
|
|
151
|
+
- No OpenClaw core changes are required or included. The plugin relies entirely on the native hook lifecycle exposed by the OpenClaw Plugin SDK.
|
|
152
|
+
|
|
153
|
+
## Development
|
|
154
|
+
|
|
155
|
+
Prerequisites:
|
|
156
|
+
|
|
157
|
+
- Node.js `>=22.12.0`
|
|
158
|
+
- npm `>=10`
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
npm ci
|
|
162
|
+
npm run lint
|
|
163
|
+
npm run typecheck
|
|
164
|
+
npm run test
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Log lines from the plugin are prefixed with `[atheon-openclaw]` for easy filtering:
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
openclaw gateway run 2>&1 | grep '\[atheon-openclaw\]'
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## License
|
|
174
|
+
|
|
175
|
+
This SDK is licensed under the **Apache License 2.0**. See [LICENSE](LICENSE) for details.
|
|
176
|
+
|
|
177
|
+
## Links
|
|
178
|
+
|
|
179
|
+
- [Atheon Documentation](https://docs.atheon.ad)
|
|
180
|
+
- [Gateway Dashboard](https://gateway.atheon.ad)
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import*as I from"@atheon-inc/codex";import{sanitizeValue as f}from"../payload_sanitizer.js";import{normalizeProvider as y,resolveChannelId as k,resolveTrigger as A,formatError as c}from"../utils.js";function R(i){i.api.on("llm_input",(n,s)=>{const e=s.sessionKey;if(!e)return;const t=i.registry.getNode(e);if(t!==void 0&&t.parentSessionKey!==void 0){t.lastActivityAt=Date.now();const l=y(n.provider)??n.provider??"unknown",h=n.model??"unknown";try{t.interaction.setProviderAndModelName({provider:l,modelName:h})}catch(w){i.logger.warn(`[atheon-openclaw] failed to update provider/model: ${c(w)}`)}i.registry.linkAgent(e,s.agentId);return}const p=y(n.provider)??n.provider??"unknown",m=n.model??"unknown",u={...n.sessionId?{sessionId:n.sessionId}:{},...n.runId?{runId:n.runId}:{},...s.agentId?{agentId:s.agentId}:{}},r=k(s);r&&(u.channel=r);const a=A(s);a&&(u.trigger=a);const o=f({prompt:n.prompt,systemPrompt:n.systemPrompt,historyMessages:n.historyMessages,imagesCount:n.imagesCount});let g;try{[g]=I.begin({provider:p,modelName:m,input:typeof o.prompt=="string"?o.prompt:JSON.stringify(o.prompt),conversationId:e,properties:u})}catch(l){i.logger.warn(`[atheon-openclaw] interaction begin failed (sessionKey=${e}): ${c(l)}`);return}const d=i.registry.createRootNode(e,g);i.registry.linkAgent(d.sessionKey,s.agentId)}),i.api.on("llm_output",(n,s)=>{const e=s.sessionKey;if(!e)return;i.registry.linkAgent(e,s.agentId);const t=i.registry.getNode(e);if(!t||t.status!=="ACTIVE")return;t.lastActivityAt=Date.now();const p=f({assistantTexts:n.assistantTexts,lastAssistant:n.lastAssistant}),u=(Array.isArray(p.assistantTexts)?p.assistantTexts.filter(d=>typeof d=="string"):[]).join(`
|
|
2
|
+
|
|
3
|
+
`),r=n.usage,a=typeof r?.input=="number"?r.input:void 0,o=typeof r?.output=="number"?r.output:void 0;if(t.parentSessionKey!==void 0){t.outputText=null;try{t.interaction.setMetrics({...a!==void 0?{tokensInput:a}:{},...o!==void 0?{tokensOutput:o}:{},finishReason:"stop"})}catch(d){i.logger.warn(`[atheon-openclaw] failed to set subagent metrics: ${c(d)}`)}return}t.outputText=u;const g={output:u,finishReason:"stop",...a!==void 0?{tokensInput:a}:{},...o!==void 0?{tokensOutput:o}:{}};if(t.toolTimings.size>0){t.pendingFinish!==void 0&&i.logger.warn(`[atheon-openclaw] llm_output received while pendingFinish already set (sessionKey=${e}) \u2014 overwriting previous pending finish`),t.pendingFinish=g;return}try{t.interaction.finish(g)}catch(d){i.logger.warn(`[atheon-openclaw] interaction finish failed (sessionKey=${e}): ${c(d)}`)}finally{i.registry.removeNode(e)}})}export{R as registerLlmHooks};
|
|
4
|
+
//# sourceMappingURL=llm.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/hooks/llm.ts"],
|
|
4
|
+
"sourcesContent": ["import type { OpenClawPluginApi } from \"openclaw/plugin-sdk/core\";\nimport type { Interaction, ChildInteraction } from \"@atheon-inc/codex\";\nimport * as atheon from \"@atheon-inc/codex\";\nimport { sanitizeValue } from \"../payload_sanitizer.js\";\nimport type { SessionRegistry } from \"../session_registry.js\";\nimport {\n normalizeProvider,\n resolveChannelId,\n resolveTrigger,\n formatError,\n} from \"../utils.js\";\n\ntype LlmHooksDeps = {\n api: OpenClawPluginApi;\n registry: SessionRegistry;\n logger: {\n warn: (message: string) => void;\n };\n};\n\nexport function registerLlmHooks(deps: LlmHooksDeps): void {\n deps.api.on(\"llm_input\", (event, agentCtx) => {\n const sessionKey = agentCtx.sessionKey;\n if (!sessionKey) return;\n\n const existingNode = deps.registry.getNode(sessionKey);\n\n if (\n existingNode !== undefined &&\n existingNode.parentSessionKey !== undefined\n ) {\n existingNode.lastActivityAt = Date.now();\n\n const provider =\n normalizeProvider(event.provider) ??\n (event.provider as string) ??\n \"unknown\";\n const modelName: string = event.model ?? \"unknown\";\n\n try {\n existingNode.interaction.setProviderAndModelName({\n provider,\n modelName,\n });\n } catch (err) {\n deps.logger.warn(\n `[atheon-openclaw] failed to update provider/model: ${formatError(err)}`,\n );\n }\n\n deps.registry.linkAgent(sessionKey, agentCtx.agentId);\n return;\n }\n\n const provider =\n normalizeProvider(event.provider) ??\n (event.provider as string) ??\n \"unknown\";\n const modelName: string = event.model ?? \"unknown\";\n\n const properties: Record<string, unknown> = {\n ...(event.sessionId ? { sessionId: event.sessionId } : {}),\n ...(event.runId ? { runId: event.runId } : {}),\n ...(agentCtx.agentId ? { agentId: agentCtx.agentId } : {}),\n };\n\n const channelId = resolveChannelId(agentCtx as Record<string, unknown>);\n if (channelId) properties.channel = channelId;\n\n const trigger = resolveTrigger(agentCtx as Record<string, unknown>);\n if (trigger) properties.trigger = trigger;\n\n const sanitizedLlmInput = sanitizeValue({\n prompt: event.prompt,\n systemPrompt: event.systemPrompt,\n historyMessages: event.historyMessages,\n imagesCount: event.imagesCount,\n }) as Record<string, unknown>;\n\n let interaction: Interaction;\n\n try {\n [interaction] = atheon.begin({\n provider,\n modelName,\n input:\n typeof sanitizedLlmInput.prompt === \"string\"\n ? sanitizedLlmInput.prompt\n : JSON.stringify(sanitizedLlmInput.prompt),\n conversationId: sessionKey,\n properties,\n });\n } catch (err) {\n deps.logger.warn(\n `[atheon-openclaw] interaction begin failed (sessionKey=${sessionKey}): ${formatError(err)}`,\n );\n return;\n }\n\n const node = deps.registry.createRootNode(sessionKey, interaction);\n deps.registry.linkAgent(node.sessionKey, agentCtx.agentId);\n });\n\n deps.api.on(\"llm_output\", (event, agentCtx) => {\n const sessionKey = agentCtx.sessionKey;\n if (!sessionKey) return;\n\n deps.registry.linkAgent(sessionKey, agentCtx.agentId);\n\n const node = deps.registry.getNode(sessionKey);\n if (!node || node.status !== \"ACTIVE\") return;\n\n node.lastActivityAt = Date.now();\n\n const sanitizedLlmOutput = sanitizeValue({\n assistantTexts: event.assistantTexts,\n lastAssistant: event.lastAssistant,\n }) as { assistantTexts?: unknown };\n\n const sanitizedAssistantTexts = Array.isArray(\n sanitizedLlmOutput.assistantTexts,\n )\n ? sanitizedLlmOutput.assistantTexts.filter(\n (item): item is string => typeof item === \"string\",\n )\n : [];\n const outputText = sanitizedAssistantTexts.join(\"\\n\\n\");\n\n const usage = event.usage as Record<string, unknown> | undefined;\n const tokensInput =\n typeof usage?.input === \"number\" ? usage.input : undefined;\n const tokensOutput =\n typeof usage?.output === \"number\" ? usage.output : undefined;\n\n if (node.parentSessionKey !== undefined) {\n node.outputText = null;\n\n try {\n (node.interaction as ChildInteraction).setMetrics({\n ...(tokensInput !== undefined ? { tokensInput } : {}),\n ...(tokensOutput !== undefined ? { tokensOutput } : {}),\n finishReason: \"stop\",\n });\n } catch (err) {\n deps.logger.warn(\n `[atheon-openclaw] failed to set subagent metrics: ${formatError(err)}`,\n );\n }\n\n return;\n }\n\n node.outputText = outputText;\n\n const finishArgs = {\n output: outputText,\n finishReason: \"stop\" as const,\n ...(tokensInput !== undefined ? { tokensInput } : {}),\n ...(tokensOutput !== undefined ? { tokensOutput } : {}),\n };\n\n if (node.toolTimings.size > 0) {\n if (node.pendingFinish !== undefined) {\n deps.logger.warn(\n `[atheon-openclaw] llm_output received while pendingFinish already set (sessionKey=${sessionKey}) \u2014 overwriting previous pending finish`,\n );\n }\n node.pendingFinish = finishArgs;\n return;\n }\n\n try {\n (node.interaction as Interaction).finish(finishArgs);\n } catch (err) {\n deps.logger.warn(\n `[atheon-openclaw] interaction finish failed (sessionKey=${sessionKey}): ${formatError(err)}`,\n );\n } finally {\n deps.registry.removeNode(sessionKey);\n }\n });\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["atheon", "sanitizeValue", "normalizeProvider", "resolveChannelId", "resolveTrigger", "formatError", "registerLlmHooks", "deps", "event", "agentCtx", "sessionKey", "existingNode", "provider", "modelName", "err", "properties", "channelId", "trigger", "sanitizedLlmInput", "interaction", "node", "sanitizedLlmOutput", "outputText", "item", "usage", "tokensInput", "tokensOutput", "finishArgs"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{nonEmptyString as i,formatError as l}from"../utils.js";function p(t){t.api.on("subagent_spawning",(e,s)=>{const n=i(s.requesterSessionKey),o=i(e.childSessionKey)??i(s.childSessionKey);if(!o||!n)return;const r=t.registry.getNode(n);if(!r||r.status!=="ACTIVE")return;r.lastActivityAt=Date.now();const a=i(e.agentId)??"subagent";let c;try{[c]=r.interaction.spawnChildInteraction({agentName:a,parent:r.interaction,properties:{childSessionKey:o,requesterSessionKey:n,mode:e.mode,label:e.label}})}catch(d){t.logger.warn(`[atheon-openclaw] spawnChildInteraction failed (childSessionKey=${o}): ${l(d)}`);return}if(!t.registry.createChildNode(o,n,c)){t.logger.warn(`[atheon-openclaw] createChildNode returned undefined \u2014 parent not found (childSessionKey=${o})`);return}}),t.api.on("subagent_spawned",(e,s)=>{const n=i(s.requesterSessionKey),o=i(e.childSessionKey)??i(s.childSessionKey);if(!o||!n)return;const r=t.registry.getNode(o);if(!r||r.status!=="ACTIVE"||r.parentSessionKey!==n)return;r.lastActivityAt=Date.now();const a=t.registry.getNode(n);a&&(a.lastActivityAt=Date.now());try{r.interaction.setProperty("runId",i(e.runId)??i(s.runId)),r.interaction.setProperty("spawnedAgentId",e.agentId),r.interaction.setProperty("threadRequested",e.threadRequested)}catch(c){t.logger.warn(`[atheon-openclaw] failed to set subagent properties: ${l(c)}`)}}),t.api.on("subagent_ended",(e,s)=>{const n=i(s.requesterSessionKey);if(!n)return;const o=t.registry.getNode(n);if(!o||o.status!=="ACTIVE")return;o.lastActivityAt=Date.now();const r=[i(s.childSessionKey),i(e.targetSessionKey)],a=r.find(d=>{if(d===void 0)return!1;const y=t.registry.getNode(d);return y!==void 0&&y.parentSessionKey===n});if(!a){t.logger.warn(`[atheon-openclaw] subagent_ended could not resolve child (requesterSessionKey=${n}, candidates=${JSON.stringify(r)})`);return}const c=t.registry.getNode(a);try{c.interaction.setProperty("outcome",e.outcome),c.interaction.setProperty("reason",e.reason),e.endedAt&&c.interaction.setProperty("endedAt",e.endedAt)}catch(d){t.logger.warn(`[atheon-openclaw] failed to set subagent properties: ${l(d)}`)}const g=typeof e.error=="string"&&e.error.length>0?e.error:void 0;t.registry.closeChild(a,g)})}export{p as registerSubagentHooks};
|
|
2
|
+
//# sourceMappingURL=subagent.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/hooks/subagent.ts"],
|
|
4
|
+
"sourcesContent": ["import type { OpenClawPluginApi } from \"openclaw/plugin-sdk/core\";\nimport type { ChildInteraction } from \"@atheon-inc/codex\";\nimport type { SessionRegistry } from \"../session_registry.js\";\nimport { nonEmptyString, formatError } from \"../utils.js\";\n\ntype SubagentHooksDeps = {\n api: OpenClawPluginApi;\n registry: SessionRegistry;\n logger: {\n warn: (message: string) => void;\n };\n};\n\nexport function registerSubagentHooks(deps: SubagentHooksDeps): void {\n deps.api.on(\"subagent_spawning\", (event, subagentCtx) => {\n const requesterSessionKey = nonEmptyString(subagentCtx.requesterSessionKey);\n const childSessionKey =\n nonEmptyString(event.childSessionKey) ??\n nonEmptyString(subagentCtx.childSessionKey);\n\n if (!childSessionKey || !requesterSessionKey) return;\n\n const parentNode = deps.registry.getNode(requesterSessionKey);\n if (!parentNode || parentNode.status !== \"ACTIVE\") return;\n\n parentNode.lastActivityAt = Date.now();\n\n const agentName = nonEmptyString(event.agentId) ?? \"subagent\";\n\n let childInteraction: ChildInteraction;\n\n try {\n [childInteraction] = parentNode.interaction.spawnChildInteraction({\n agentName,\n parent: parentNode.interaction,\n properties: {\n childSessionKey,\n requesterSessionKey,\n mode: event.mode,\n label: event.label,\n },\n });\n } catch (err) {\n deps.logger.warn(\n `[atheon-openclaw] spawnChildInteraction failed (childSessionKey=${childSessionKey}): ${formatError(err)}`,\n );\n return;\n }\n\n const childNode = deps.registry.createChildNode(\n childSessionKey,\n requesterSessionKey,\n childInteraction,\n );\n\n if (!childNode) {\n deps.logger.warn(\n `[atheon-openclaw] createChildNode returned undefined \u2014 parent not found (childSessionKey=${childSessionKey})`,\n );\n return;\n }\n });\n\n deps.api.on(\"subagent_spawned\", (event, subagentCtx) => {\n const requesterSessionKey = nonEmptyString(subagentCtx.requesterSessionKey);\n const childSessionKey =\n nonEmptyString(event.childSessionKey) ??\n nonEmptyString(subagentCtx.childSessionKey);\n if (!childSessionKey || !requesterSessionKey) return;\n\n const childNode = deps.registry.getNode(childSessionKey);\n if (\n !childNode ||\n childNode.status !== \"ACTIVE\" ||\n childNode.parentSessionKey !== requesterSessionKey\n )\n return;\n\n childNode.lastActivityAt = Date.now();\n\n const parentNode = deps.registry.getNode(requesterSessionKey);\n if (parentNode) parentNode.lastActivityAt = Date.now();\n\n try {\n childNode.interaction.setProperty(\n \"runId\",\n nonEmptyString(event.runId) ?? nonEmptyString(subagentCtx.runId),\n );\n childNode.interaction.setProperty(\"spawnedAgentId\", event.agentId);\n childNode.interaction.setProperty(\n \"threadRequested\",\n event.threadRequested,\n );\n } catch (err) {\n deps.logger.warn(\n `[atheon-openclaw] failed to set subagent properties: ${formatError(err)}`,\n );\n }\n });\n\n deps.api.on(\"subagent_ended\", (event, subagentCtx) => {\n const requesterSessionKey = nonEmptyString(subagentCtx.requesterSessionKey);\n if (!requesterSessionKey) return;\n\n const parentNode = deps.registry.getNode(requesterSessionKey);\n if (!parentNode || parentNode.status !== \"ACTIVE\") return;\n\n parentNode.lastActivityAt = Date.now();\n\n const candidateKeys = [\n nonEmptyString(subagentCtx.childSessionKey),\n nonEmptyString(event.targetSessionKey),\n ];\n\n const resolvedKey = candidateKeys.find((key): key is string => {\n if (key === undefined) return false;\n const node = deps.registry.getNode(key);\n return (\n node !== undefined && node.parentSessionKey === requesterSessionKey\n );\n });\n\n if (!resolvedKey) {\n deps.logger.warn(\n `[atheon-openclaw] subagent_ended could not resolve child (requesterSessionKey=${requesterSessionKey}, candidates=${JSON.stringify(candidateKeys)})`,\n );\n return;\n }\n\n const childNode = deps.registry.getNode(resolvedKey)!;\n\n try {\n childNode.interaction.setProperty(\"outcome\", event.outcome);\n childNode.interaction.setProperty(\"reason\", event.reason);\n if (event.endedAt)\n childNode.interaction.setProperty(\"endedAt\", event.endedAt);\n } catch (err) {\n deps.logger.warn(\n `[atheon-openclaw] failed to set subagent properties: ${formatError(err)}`,\n );\n }\n\n const errorText =\n typeof event.error === \"string\" && event.error.length > 0\n ? event.error\n : undefined;\n\n deps.registry.closeChild(resolvedKey, errorText);\n });\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["nonEmptyString", "formatError", "registerSubagentHooks", "deps", "event", "subagentCtx", "requesterSessionKey", "childSessionKey", "parentNode", "agentName", "childInteraction", "err", "childNode", "candidateKeys", "resolvedKey", "key", "node", "errorText"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{resolveToolCallId as u,buildSpanKey as T,formatError as h}from"../utils.js";import{randomUUID as _}from"node:crypto";function N(e){let d=0;const f=5;e.api.on("before_tool_call",(r,o)=>{const t=o.sessionKey;if(!t)return;const i=e.registry.resolveToolTarget(t);if(!i)return;e.registry.linkAgent(i.rootNode.sessionKey,o.agentId),i.node.lastActivityAt=Date.now();const s=r.toolName,a=u(r,o),n=T(t,s,a),l=i.node.toolTimings.get(n);l?l.startTimes.push(performance.now()):i.node.toolTimings.set(n,{toolName:s,startTimes:[performance.now()]})}),e.api.on("after_tool_call",(r,o)=>{const t=r.toolName??"unknown",i=u(r,o);let s=o.sessionKey;if(!s){const c=typeof o.agentId=="string"?e.registry.resolveSessionKey(o.agentId):void 0;if(c!==void 0&&(s=c,d<f)){d++;const K=d===f?" (further occurrences suppressed)":"";e.logger.warn(`[atheon-openclaw] after_tool_call missing sessionKey \u2014 resolved via agentId (tool=${t})${K}`)}}if(!s)return;const a=e.registry.resolveToolTarget(s);if(!a)return;const{node:n,rootNode:l}=a;e.registry.linkAgent(l.sessionKey,o.agentId),n.lastActivityAt=Date.now();const m=T(s,t,i);let y;const g=n.toolTimings.get(m);g&&g.startTimes.length>0&&(y=g.startTimes.shift(),g.startTimes.length===0&&n.toolTimings.delete(m));const p=r.durationMs??(y!==void 0?performance.now()-y:0),I=r.error||void 0,w={id:_(),type:"tool",name:t,latency_ms:String(p),error:I};try{n.interaction.addToolExecution(w)}catch(c){e.logger.warn(`[atheon-openclaw] addToolExecution failed (tool=${t}): ${h(c)}`)}n!==l&&e.registry.tryFlushPendingClose(n),e.registry.tryFlushPendingFinish(l)})}export{N as registerToolHooks};
|
|
2
|
+
//# sourceMappingURL=tool.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/hooks/tool.ts"],
|
|
4
|
+
"sourcesContent": ["import type { OpenClawPluginApi } from \"openclaw/plugin-sdk/core\";\nimport type { ToolRecord } from \"@atheon-inc/codex\";\nimport type { SessionRegistry } from \"../session_registry.js\";\nimport { resolveToolCallId, buildSpanKey, formatError } from \"../utils.js\";\nimport { randomUUID } from \"node:crypto\";\n\ntype ToolHooksDeps = {\n api: OpenClawPluginApi;\n registry: SessionRegistry;\n logger: {\n warn: (message: string) => void;\n };\n};\n\nexport function registerToolHooks(deps: ToolHooksDeps): void {\n let missingSessionKeyWarnCount = 0;\n const MISSING_SESSION_KEY_WARN_LIMIT = 5;\n\n deps.api.on(\"before_tool_call\", (event, toolCtx) => {\n const sessionKey = toolCtx.sessionKey;\n if (!sessionKey) return;\n\n const resolved = deps.registry.resolveToolTarget(sessionKey);\n if (!resolved) return;\n\n deps.registry.linkAgent(resolved.rootNode.sessionKey, toolCtx.agentId);\n resolved.node.lastActivityAt = Date.now();\n\n const toolName = event.toolName as string;\n const toolCallId = resolveToolCallId(event, toolCtx);\n const spanKey = buildSpanKey(sessionKey, toolName, toolCallId);\n\n const existing = resolved.node.toolTimings.get(spanKey);\n if (existing) {\n existing.startTimes.push(performance.now());\n } else {\n resolved.node.toolTimings.set(spanKey, {\n toolName,\n startTimes: [performance.now()],\n });\n }\n });\n\n deps.api.on(\"after_tool_call\", (event, toolCtx) => {\n const toolName: string = (event.toolName as string) ?? \"unknown\";\n const toolCallId = resolveToolCallId(event, toolCtx);\n\n let sessionKey: string | undefined = toolCtx.sessionKey;\n\n if (!sessionKey) {\n const byAgentId =\n typeof toolCtx.agentId === \"string\"\n ? deps.registry.resolveSessionKey(toolCtx.agentId)\n : undefined;\n\n if (byAgentId !== undefined) {\n sessionKey = byAgentId;\n\n if (missingSessionKeyWarnCount < MISSING_SESSION_KEY_WARN_LIMIT) {\n missingSessionKeyWarnCount++;\n const suffix =\n missingSessionKeyWarnCount === MISSING_SESSION_KEY_WARN_LIMIT\n ? \" (further occurrences suppressed)\"\n : \"\";\n deps.logger.warn(\n `[atheon-openclaw] after_tool_call missing sessionKey \u2014 resolved via agentId (tool=${toolName})${suffix}`,\n );\n }\n }\n }\n\n if (!sessionKey) return;\n\n const resolved = deps.registry.resolveToolTarget(sessionKey);\n if (!resolved) return;\n\n const { node, rootNode } = resolved;\n deps.registry.linkAgent(rootNode.sessionKey, toolCtx.agentId);\n node.lastActivityAt = Date.now();\n\n const spanKey = buildSpanKey(sessionKey, toolName, toolCallId);\n let startTime: number | undefined;\n\n const toolTimingQueue = node.toolTimings.get(spanKey);\n if (toolTimingQueue && toolTimingQueue.startTimes.length > 0) {\n startTime = toolTimingQueue.startTimes.shift();\n if (toolTimingQueue.startTimes.length === 0) {\n node.toolTimings.delete(spanKey);\n }\n }\n\n const latencyMs =\n event.durationMs ??\n (startTime !== undefined ? performance.now() - startTime : 0);\n\n const errorText = event.error || undefined;\n\n const record: ToolRecord = {\n id: randomUUID(),\n type: \"tool\",\n name: toolName,\n latency_ms: String(latencyMs),\n error: errorText,\n };\n\n try {\n node.interaction.addToolExecution(record);\n } catch (err) {\n deps.logger.warn(\n `[atheon-openclaw] addToolExecution failed (tool=${toolName}): ${formatError(err)}`,\n );\n }\n\n if (node !== rootNode) {\n deps.registry.tryFlushPendingClose(node);\n }\n\n deps.registry.tryFlushPendingFinish(rootNode);\n });\n}\n"],
|
|
5
|
+
"mappings": "AAGA,OAAS,qBAAAA,EAAmB,gBAAAC,EAAc,eAAAC,MAAmB,cAC7D,OAAS,cAAAC,MAAkB,cAUpB,SAASC,EAAkBC,EAA2B,CAC3D,IAAIC,EAA6B,EACjC,MAAMC,EAAiC,EAEvCF,EAAK,IAAI,GAAG,mBAAoB,CAACG,EAAOC,IAAY,CAClD,MAAMC,EAAaD,EAAQ,WAC3B,GAAI,CAACC,EAAY,OAEjB,MAAMC,EAAWN,EAAK,SAAS,kBAAkBK,CAAU,EAC3D,GAAI,CAACC,EAAU,OAEfN,EAAK,SAAS,UAAUM,EAAS,SAAS,WAAYF,EAAQ,OAAO,EACrEE,EAAS,KAAK,eAAiB,KAAK,IAAI,EAExC,MAAMC,EAAWJ,EAAM,SACjBK,EAAab,EAAkBQ,EAAOC,CAAO,EAC7CK,EAAUb,EAAaS,EAAYE,EAAUC,CAAU,EAEvDE,EAAWJ,EAAS,KAAK,YAAY,IAAIG,CAAO,EAClDC,EACFA,EAAS,WAAW,KAAK,YAAY,IAAI,CAAC,EAE1CJ,EAAS,KAAK,YAAY,IAAIG,EAAS,CACrC,SAAAF,EACA,WAAY,CAAC,YAAY,IAAI,CAAC,CAChC,CAAC,CAEL,CAAC,EAEDP,EAAK,IAAI,GAAG,kBAAmB,CAACG,EAAOC,IAAY,CACjD,MAAMG,EAAoBJ,EAAM,UAAuB,UACjDK,EAAab,EAAkBQ,EAAOC,CAAO,EAEnD,IAAIC,EAAiCD,EAAQ,WAE7C,GAAI,CAACC,EAAY,CACf,MAAMM,EACJ,OAAOP,EAAQ,SAAY,SACvBJ,EAAK,SAAS,kBAAkBI,EAAQ,OAAO,EAC/C,OAEN,GAAIO,IAAc,SAChBN,EAAaM,EAETV,EAA6BC,GAAgC,CAC/DD,IACA,MAAMW,EACJX,IAA+BC,EAC3B,oCACA,GACNF,EAAK,OAAO,KACV,0FAAqFO,CAAQ,IAAIK,CAAM,EACzG,CACF,CAEJ,CAEA,GAAI,CAACP,EAAY,OAEjB,MAAMC,EAAWN,EAAK,SAAS,kBAAkBK,CAAU,EAC3D,GAAI,CAACC,EAAU,OAEf,KAAM,CAAE,KAAAO,EAAM,SAAAC,CAAS,EAAIR,EAC3BN,EAAK,SAAS,UAAUc,EAAS,WAAYV,EAAQ,OAAO,EAC5DS,EAAK,eAAiB,KAAK,IAAI,EAE/B,MAAMJ,EAAUb,EAAaS,EAAYE,EAAUC,CAAU,EAC7D,IAAIO,EAEJ,MAAMC,EAAkBH,EAAK,YAAY,IAAIJ,CAAO,EAChDO,GAAmBA,EAAgB,WAAW,OAAS,IACzDD,EAAYC,EAAgB,WAAW,MAAM,EACzCA,EAAgB,WAAW,SAAW,GACxCH,EAAK,YAAY,OAAOJ,CAAO,GAInC,MAAMQ,EACJd,EAAM,aACLY,IAAc,OAAY,YAAY,IAAI,EAAIA,EAAY,GAEvDG,EAAYf,EAAM,OAAS,OAE3BgB,EAAqB,CACzB,GAAIrB,EAAW,EACf,KAAM,OACN,KAAMS,EACN,WAAY,OAAOU,CAAS,EAC5B,MAAOC,CACT,EAEA,GAAI,CACFL,EAAK,YAAY,iBAAiBM,CAAM,CAC1C,OAASC,EAAK,CACZpB,EAAK,OAAO,KACV,mDAAmDO,CAAQ,MAAMV,EAAYuB,CAAG,CAAC,EACnF,CACF,CAEIP,IAASC,GACXd,EAAK,SAAS,qBAAqBa,CAAI,EAGzCb,EAAK,SAAS,sBAAsBc,CAAQ,CAC9C,CAAC,CACH",
|
|
6
|
+
"names": ["resolveToolCallId", "buildSpanKey", "formatError", "randomUUID", "registerToolHooks", "deps", "missingSessionKeyWarnCount", "MISSING_SESSION_KEY_WARN_LIMIT", "event", "toolCtx", "sessionKey", "resolved", "toolName", "toolCallId", "spanKey", "existing", "byAgentId", "suffix", "node", "rootNode", "startTime", "toolTimingQueue", "latencyMs", "errorText", "record", "err"]
|
|
7
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -1,13 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
async function main() {
|
|
4
|
-
try {
|
|
5
|
-
await atheon.init({
|
|
6
|
-
apiKey: process.env.apiKey
|
|
7
|
-
});
|
|
8
|
-
} catch (error) {
|
|
9
|
-
return;
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
main();
|
|
1
|
+
import{definePluginEntry as a}from"openclaw/plugin-sdk/plugin-entry";import{emptyPluginConfigSchema as f}from"openclaw/plugin-sdk";import{parseRawConfigIntoAtheonCodexClientOptions as m}from"./utils.js";import{createAtheonCodexService as c}from"./service.js";import{SessionRegistry as d}from"./session_registry.js";import{registerLlmHooks as _}from"./hooks/llm.js";import{registerToolHooks as p}from"./hooks/tool.js";import{registerSubagentHooks as u}from"./hooks/subagent.js";const A="0.1.0";var E=a({id:"atheon-openclaw",name:"atheon",description:"Export LLM, Tools and SubAgent analytics to Atheon using atheon-codex",configSchema:f(),register(e){const s=m(e.pluginConfig),i=300*1e3,g=3600*1e3,r=new d(e.logger,{maxIdleTimeMs:g});let o;e.on("gateway_start",()=>{o=setInterval(()=>{r.reapStaleNodes(Date.now())},i),o?.unref&&o.unref()}),e.on("gateway_stop",()=>{o&&(clearInterval(o),o=void 0)}),_({api:e,registry:r,logger:{warn:n=>e.logger.warn(n)}}),p({api:e,registry:r,logger:{warn:n=>e.logger.warn(n)}}),u({api:e,registry:r,logger:{warn:n=>e.logger.warn(n)}}),e.on("session_end",(n,l)=>{const t=l.sessionKey;typeof t=="string"&&r.closeRoot(t,`session_end sessionKey=${t}`)}),e.registerService(c(s))}});export{A as __version__,E as default};
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/index.ts"],
|
|
4
|
+
"sourcesContent": ["import { definePluginEntry } from \"openclaw/plugin-sdk/plugin-entry\";\nimport { emptyPluginConfigSchema } from \"openclaw/plugin-sdk\";\nimport { parseRawConfigIntoAtheonCodexClientOptions } from \"./utils.js\";\nimport { createAtheonCodexService } from \"./service.js\";\nimport { SessionRegistry } from \"./session_registry.js\";\nimport { registerLlmHooks } from \"./hooks/llm.js\";\nimport { registerToolHooks } from \"./hooks/tool.js\";\nimport { registerSubagentHooks } from \"./hooks/subagent.js\";\n\nexport const __version__ = \"0.1.0\";\n\nexport default definePluginEntry({\n id: \"atheon-openclaw\",\n name: \"atheon\",\n description:\n \"Export LLM, Tools and SubAgent analytics to Atheon using atheon-codex\",\n configSchema: emptyPluginConfigSchema(),\n register(api) {\n const atheonCodexClientOptions = parseRawConfigIntoAtheonCodexClientOptions(\n api.pluginConfig,\n );\n\n const REAPER_INTERVAL_MS = 5 * 60 * 1000;\n const MAX_IDLE_TIME_MS = 60 * 60 * 1000;\n\n const registry = new SessionRegistry(api.logger, {\n maxIdleTimeMs: MAX_IDLE_TIME_MS,\n });\n\n let reaperInterval: ReturnType<typeof setInterval> | undefined;\n\n api.on(\"gateway_start\", () => {\n reaperInterval = setInterval(() => {\n registry.reapStaleNodes(Date.now());\n }, REAPER_INTERVAL_MS);\n if (reaperInterval?.unref) reaperInterval.unref();\n });\n\n api.on(\"gateway_stop\", () => {\n if (reaperInterval) {\n clearInterval(reaperInterval);\n reaperInterval = undefined;\n }\n });\n\n registerLlmHooks({\n api,\n registry,\n logger: { warn: (msg) => api.logger.warn(msg) },\n });\n\n registerToolHooks({\n api,\n registry,\n logger: { warn: (msg) => api.logger.warn(msg) },\n });\n\n registerSubagentHooks({\n api,\n registry,\n logger: { warn: (msg) => api.logger.warn(msg) },\n });\n\n api.on(\"session_end\", (_event, ctx) => {\n const sessionKey = ctx.sessionKey;\n if (typeof sessionKey !== \"string\") return;\n registry.closeRoot(sessionKey, `session_end sessionKey=${sessionKey}`);\n });\n\n api.registerService(createAtheonCodexService(atheonCodexClientOptions));\n },\n});\n"],
|
|
5
|
+
"mappings": "AAAA,OAAS,qBAAAA,MAAyB,mCAClC,OAAS,2BAAAC,MAA+B,sBACxC,OAAS,8CAAAC,MAAkD,aAC3D,OAAS,4BAAAC,MAAgC,eACzC,OAAS,mBAAAC,MAAuB,wBAChC,OAAS,oBAAAC,MAAwB,iBACjC,OAAS,qBAAAC,MAAyB,kBAClC,OAAS,yBAAAC,MAA6B,sBAE/B,MAAMC,EAAc,QAE3B,IAAOC,EAAQT,EAAkB,CAC/B,GAAI,kBACJ,KAAM,SACN,YACE,wEACF,aAAcC,EAAwB,EACtC,SAASS,EAAK,CACZ,MAAMC,EAA2BT,EAC/BQ,EAAI,YACN,EAEME,EAAqB,IAAS,IAC9BC,EAAmB,KAAU,IAE7BC,EAAW,IAAIV,EAAgBM,EAAI,OAAQ,CAC/C,cAAeG,CACjB,CAAC,EAED,IAAIE,EAEJL,EAAI,GAAG,gBAAiB,IAAM,CAC5BK,EAAiB,YAAY,IAAM,CACjCD,EAAS,eAAe,KAAK,IAAI,CAAC,CACpC,EAAGF,CAAkB,EACjBG,GAAgB,OAAOA,EAAe,MAAM,CAClD,CAAC,EAEDL,EAAI,GAAG,eAAgB,IAAM,CACvBK,IACF,cAAcA,CAAc,EAC5BA,EAAiB,OAErB,CAAC,EAEDV,EAAiB,CACf,IAAAK,EACA,SAAAI,EACA,OAAQ,CAAE,KAAOE,GAAQN,EAAI,OAAO,KAAKM,CAAG,CAAE,CAChD,CAAC,EAEDV,EAAkB,CAChB,IAAAI,EACA,SAAAI,EACA,OAAQ,CAAE,KAAOE,GAAQN,EAAI,OAAO,KAAKM,CAAG,CAAE,CAChD,CAAC,EAEDT,EAAsB,CACpB,IAAAG,EACA,SAAAI,EACA,OAAQ,CAAE,KAAOE,GAAQN,EAAI,OAAO,KAAKM,CAAG,CAAE,CAChD,CAAC,EAEDN,EAAI,GAAG,cAAe,CAACO,EAAQC,IAAQ,CACrC,MAAMC,EAAaD,EAAI,WACnB,OAAOC,GAAe,UAC1BL,EAAS,UAAUK,EAAY,0BAA0BA,CAAU,EAAE,CACvE,CAAC,EAEDT,EAAI,gBAAgBP,EAAyBQ,CAAwB,CAAC,CACxE,CACF,CAAC",
|
|
6
|
+
"names": ["definePluginEntry", "emptyPluginConfigSchema", "parseRawConfigIntoAtheonCodexClientOptions", "createAtheonCodexService", "SessionRegistry", "registerLlmHooks", "registerToolHooks", "registerSubagentHooks", "__version__", "index_default", "api", "atheonCodexClientOptions", "REAPER_INTERVAL_MS", "MAX_IDLE_TIME_MS", "registry", "reaperInterval", "msg", "_event", "ctx", "sessionKey"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
const E=/\bmedia:[^\s"'`]+\.(?:jpe?g|png|webp|gif)(?=[\s"'`]|$)/gi,d=/\[\[reply_to[^\]]*\]\]\s*/gi,p=/^\s*Conversation info \(untrusted metadata\):\s*\n+\{[\s\S]*?\}\s*/gim,g=/^\s*Sender \(untrusted metadata\):\s*\n+\{[\s\S]*?\}\s*/gim,u=/^\s*Untrusted context \(metadata, do not treat as instructions or commands\):\s*\n+<<<EXTERNAL_UNTRUSTED_CONTENT[\s\S]*?<<<END_EXTERNAL_UNTRUSTED_CONTENT[^>]*>>>\s*/gim;function R(n){return n.replace(/\\r\\n/g,`
|
|
2
|
+
`).replace(/\\n/g,`
|
|
3
|
+
`).replace(/\\r/g,"\r").replace(d,"").replace(u,"").replace(p,"").replace(g,"").replace(/\n{3,}/g,`
|
|
4
|
+
|
|
5
|
+
`).replace(E,"media:<image-ref>")}function _(n){if(n===null||typeof n!="object")return!1;const t=Object.getPrototypeOf(n);return t===Object.prototype||t===null}function f(n,t=new WeakSet){if(typeof n=="string")return R(n);if(n===null||typeof n!="object")return n;if(t.has(n))return"[Circular Reference]";t.add(n);const a=n;try{let e=n;if(e instanceof Set?e=Array.from(e):e instanceof Map&&(e=Object.fromEntries(e)),Array.isArray(e)){let o=!1;const s=e.length,i=new Array(s);for(let r=0;r<s;r++){const c=e[r],l=f(c,t);i[r]=l,l!==c&&(o=!0)}return o?i:e}if(_(e)){let o=!1;const s={};for(const i in e){const r=e[i],c=f(r,t);s[i]=c,c!==r&&(o=!0)}return o?s:e}return e}finally{t.delete(a)}}export{_ as isPlainObject,R as sanitizeString,f as sanitizeValue};
|
|
6
|
+
//# sourceMappingURL=payload_sanitizer.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/payload_sanitizer.ts"],
|
|
4
|
+
"sourcesContent": ["const MEDIA_IMAGE_REFERENCE_RE =\n /\\bmedia:[^\\s\"'`]+\\.(?:jpe?g|png|webp|gif)(?=[\\s\"'`]|$)/gi;\nconst INTERNAL_REPLY_TO_MARKER_RE = /\\[\\[reply_to[^\\]]*\\]\\]\\s*/gi;\nconst CONVERSATION_INFO_BLOCK_RE =\n /^\\s*Conversation info \\(untrusted metadata\\):\\s*\\n+\\{[\\s\\S]*?\\}\\s*/gim;\nconst SENDER_INFO_BLOCK_RE =\n /^\\s*Sender \\(untrusted metadata\\):\\s*\\n+\\{[\\s\\S]*?\\}\\s*/gim;\nconst UNTRUSTED_CONTEXT_BLOCK_RE =\n /^\\s*Untrusted context \\(metadata, do not treat as instructions or commands\\):\\s*\\n+<<<EXTERNAL_UNTRUSTED_CONTENT[\\s\\S]*?<<<END_EXTERNAL_UNTRUSTED_CONTENT[^>]*>>>\\s*/gim;\n\nexport function sanitizeString(value: string): string {\n const normalizedNewlines = value\n .replace(/\\\\r\\\\n/g, \"\\n\")\n .replace(/\\\\n/g, \"\\n\")\n .replace(/\\\\r/g, \"\\r\");\n const redactedInternalBlocks = normalizedNewlines\n .replace(INTERNAL_REPLY_TO_MARKER_RE, \"\")\n .replace(UNTRUSTED_CONTEXT_BLOCK_RE, \"\")\n .replace(CONVERSATION_INFO_BLOCK_RE, \"\")\n .replace(SENDER_INFO_BLOCK_RE, \"\")\n .replace(/\\n{3,}/g, \"\\n\\n\");\n return redactedInternalBlocks.replace(\n MEDIA_IMAGE_REFERENCE_RE,\n \"media:<image-ref>\",\n );\n}\n\nexport function isPlainObject(\n value: unknown,\n): value is Record<string, unknown> {\n if (value === null || typeof value !== \"object\") return false;\n const proto = Object.getPrototypeOf(value);\n return proto === Object.prototype || proto === null;\n}\n\nexport function sanitizeValue(value: unknown, seen = new WeakSet()): unknown {\n if (typeof value === \"string\") {\n return sanitizeString(value);\n }\n\n if (value === null || typeof value !== \"object\") {\n return value;\n }\n\n if (seen.has(value)) {\n return \"[Circular Reference]\";\n }\n\n seen.add(value);\n const originalRef = value;\n\n try {\n let processableValue = value;\n\n if (processableValue instanceof Set) {\n processableValue = Array.from(processableValue);\n } else if (processableValue instanceof Map) {\n processableValue = Object.fromEntries(processableValue);\n }\n\n if (Array.isArray(processableValue)) {\n let changed = false;\n const len = processableValue.length;\n const next = new Array(len);\n\n for (let idx = 0; idx < len; idx++) {\n const item = processableValue[idx];\n const sanitized = sanitizeValue(item, seen);\n next[idx] = sanitized;\n if (sanitized !== item) changed = true;\n }\n return changed ? next : processableValue;\n }\n\n if (isPlainObject(processableValue)) {\n let changed = false;\n const next: Record<string, unknown> = {};\n\n for (const key in processableValue) {\n const child = processableValue[key];\n const sanitized = sanitizeValue(child, seen);\n next[key] = sanitized;\n if (sanitized !== child) changed = true;\n }\n return changed ? next : processableValue;\n }\n\n return processableValue;\n } finally {\n seen.delete(originalRef);\n }\n}\n"],
|
|
5
|
+
"mappings": "AAAA,MAAMA,EACJ,2DACIC,EAA8B,8BAC9BC,EACJ,wEACIC,EACJ,6DACIC,EACJ,0KAEK,SAASC,EAAeC,EAAuB,CAWpD,OAV2BA,EACxB,QAAQ,UAAW;AAAA,CAAI,EACvB,QAAQ,OAAQ;AAAA,CAAI,EACpB,QAAQ,OAAQ,IAAI,EAEpB,QAAQL,EAA6B,EAAE,EACvC,QAAQG,EAA4B,EAAE,EACtC,QAAQF,EAA4B,EAAE,EACtC,QAAQC,EAAsB,EAAE,EAChC,QAAQ,UAAW;AAAA;AAAA,CAAM,EACE,QAC5BH,EACA,mBACF,CACF,CAEO,SAASO,EACdD,EACkC,CAClC,GAAIA,IAAU,MAAQ,OAAOA,GAAU,SAAU,MAAO,GACxD,MAAME,EAAQ,OAAO,eAAeF,CAAK,EACzC,OAAOE,IAAU,OAAO,WAAaA,IAAU,IACjD,CAEO,SAASC,EAAcH,EAAgBI,EAAO,IAAI,QAAoB,CAC3E,GAAI,OAAOJ,GAAU,SACnB,OAAOD,EAAeC,CAAK,EAG7B,GAAIA,IAAU,MAAQ,OAAOA,GAAU,SACrC,OAAOA,EAGT,GAAII,EAAK,IAAIJ,CAAK,EAChB,MAAO,uBAGTI,EAAK,IAAIJ,CAAK,EACd,MAAMK,EAAcL,EAEpB,GAAI,CACF,IAAIM,EAAmBN,EAQvB,GANIM,aAA4B,IAC9BA,EAAmB,MAAM,KAAKA,CAAgB,EACrCA,aAA4B,MACrCA,EAAmB,OAAO,YAAYA,CAAgB,GAGpD,MAAM,QAAQA,CAAgB,EAAG,CACnC,IAAIC,EAAU,GACd,MAAMC,EAAMF,EAAiB,OACvBG,EAAO,IAAI,MAAMD,CAAG,EAE1B,QAASE,EAAM,EAAGA,EAAMF,EAAKE,IAAO,CAClC,MAAMC,EAAOL,EAAiBI,CAAG,EAC3BE,EAAYT,EAAcQ,EAAMP,CAAI,EAC1CK,EAAKC,CAAG,EAAIE,EACRA,IAAcD,IAAMJ,EAAU,GACpC,CACA,OAAOA,EAAUE,EAAOH,CAC1B,CAEA,GAAIL,EAAcK,CAAgB,EAAG,CACnC,IAAIC,EAAU,GACd,MAAME,EAAgC,CAAC,EAEvC,UAAWI,KAAOP,EAAkB,CAClC,MAAMQ,EAAQR,EAAiBO,CAAG,EAC5BD,EAAYT,EAAcW,EAAOV,CAAI,EAC3CK,EAAKI,CAAG,EAAID,EACRA,IAAcE,IAAOP,EAAU,GACrC,CACA,OAAOA,EAAUE,EAAOH,CAC1B,CAEA,OAAOA,CACT,QAAE,CACAF,EAAK,OAAOC,CAAW,CACzB,CACF",
|
|
6
|
+
"names": ["MEDIA_IMAGE_REFERENCE_RE", "INTERNAL_REPLY_TO_MARKER_RE", "CONVERSATION_INFO_BLOCK_RE", "SENDER_INFO_BLOCK_RE", "UNTRUSTED_CONTEXT_BLOCK_RE", "sanitizeString", "value", "isPlainObject", "proto", "sanitizeValue", "seen", "originalRef", "processableValue", "changed", "len", "next", "idx", "item", "sanitized", "key", "child"]
|
|
7
|
+
}
|
package/dist/service.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import*as a from"@atheon-inc/codex";import{formatError as o}from"./utils.js";function p(e){return{id:"atheon-codex",async start({logger:t}){try{await a.init({apiKey:e.apiKey,...e.baseUrl?{baseUrl:e.baseUrl}:{},...e.uploadSize?{uploadSize:e.uploadSize}:{},...e.uploadInterval?{uploadInterval:e.uploadInterval}:{}}),t.info("[atheon-openclaw] client ready")}catch(r){t.warn(`[atheon-openclaw] client init failed \u2014 ${o(r)}`);return}},async stop(){await a.shutdown()}}}export{p as createAtheonCodexService};
|
|
2
|
+
//# sourceMappingURL=service.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/service.ts"],
|
|
4
|
+
"sourcesContent": ["import * as atheon from \"@atheon-inc/codex\";\nimport type { AtheonCodexClientOptions } from \"@atheon-inc/codex\";\nimport { OpenClawPluginService } from \"openclaw/plugin-sdk/core\";\nimport { formatError } from \"./utils.js\";\n\nexport function createAtheonCodexService(\n config: AtheonCodexClientOptions,\n): OpenClawPluginService {\n return {\n id: \"atheon-codex\",\n async start({ logger }) {\n try {\n await atheon.init({\n apiKey: config.apiKey,\n ...(config.baseUrl ? { baseUrl: config.baseUrl } : {}),\n ...(config.uploadSize ? { uploadSize: config.uploadSize } : {}),\n ...(config.uploadInterval\n ? { uploadInterval: config.uploadInterval }\n : {}),\n });\n logger.info(\"[atheon-openclaw] client ready\");\n } catch (err) {\n logger.warn(\n `[atheon-openclaw] client init failed \u2014 ${formatError(err)}`,\n );\n return;\n }\n },\n\n async stop() {\n await atheon.shutdown();\n },\n };\n}\n"],
|
|
5
|
+
"mappings": "AAAA,UAAYA,MAAY,oBAGxB,OAAS,eAAAC,MAAmB,aAErB,SAASC,EACdC,EACuB,CACvB,MAAO,CACL,GAAI,eACJ,MAAM,MAAM,CAAE,OAAAC,CAAO,EAAG,CACtB,GAAI,CACF,MAAMJ,EAAO,KAAK,CAChB,OAAQG,EAAO,OACf,GAAIA,EAAO,QAAU,CAAE,QAASA,EAAO,OAAQ,EAAI,CAAC,EACpD,GAAIA,EAAO,WAAa,CAAE,WAAYA,EAAO,UAAW,EAAI,CAAC,EAC7D,GAAIA,EAAO,eACP,CAAE,eAAgBA,EAAO,cAAe,EACxC,CAAC,CACP,CAAC,EACDC,EAAO,KAAK,gCAAgC,CAC9C,OAASC,EAAK,CACZD,EAAO,KACL,+CAA0CH,EAAYI,CAAG,CAAC,EAC5D,EACA,MACF,CACF,EAEA,MAAM,MAAO,CACX,MAAML,EAAO,SAAS,CACxB,CACF,CACF",
|
|
6
|
+
"names": ["atheon", "formatError", "createAtheonCodexService", "config", "logger", "err"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{randomUUID as d}from"node:crypto";import{formatError as l}from"./utils.js";class h{constructor(e,n){this.logger=e;this.maxIdleTimeMs=n?.maxIdleTimeMs??3600*1e3}logger;nodes=new Map;agentToSession=new Map;maxIdleTimeMs;cleanupNodeMemory(e){if(e.parentSessionKey!==void 0){const n=this.nodes.get(e.parentSessionKey);n&&n.childSessionKeys.delete(e.sessionKey)}for(const n of e.linkedAgentIds)this.agentToSession.delete(n);this.nodes.delete(e.sessionKey),e.status="CLOSED",e.linkedAgentIds.clear(),e.childSessionKeys.clear(),e.toolTimings.clear()}safeExecute(e,n){try{e()}catch(t){this.logger.warn(`[atheon-openclaw] ${n} failed: ${l(t)}`)}}createRootNode(e,n){this.nodes.get(e)&&(this.logger.warn(`[atheon-openclaw] createRootNode called for existing sessionKey=${e} \u2014 closing existing node first`),this.closeRoot(e,"replaced by new root node"));const i={sessionKey:e,parentSessionKey:void 0,status:"ACTIVE",interaction:n,outputText:"",childSessionKeys:new Set,linkedAgentIds:new Set,toolTimings:new Map,lastActivityAt:Date.now()};return this.nodes.set(e,i),i}createChildNode(e,n,t){const i=this.nodes.get(n);if(!i)return;this.nodes.get(e)&&this.closeChild(e,"subagent reset \u2014 new spawn initiated");const o={sessionKey:e,parentSessionKey:n,status:"ACTIVE",interaction:t,outputText:"",childSessionKeys:new Set,linkedAgentIds:new Set,toolTimings:new Map,lastActivityAt:Date.now()};return this.nodes.set(e,o),i.childSessionKeys.add(e),i.lastActivityAt=Date.now(),o}getNode(e){return this.nodes.get(e)}removeNode(e){const n=this.nodes.get(e);n&&this.cleanupNodeMemory(n)}linkAgent(e,n){if(typeof n!="string"||n.length===0)return;const t=this.nodes.get(e);t&&(t.linkedAgentIds.add(n),this.agentToSession.set(n,e),t.lastActivityAt=Date.now())}resolveSessionKey(e){return this.nodes.has(e)?e:this.agentToSession.get(e)}resolveToolTarget(e){const n=this.nodes.get(e);if(!n||n.status!=="ACTIVE")return;if(n.parentSessionKey===void 0)return{node:n,rootNode:n};const t=this.nodes.get(n.parentSessionKey);if(!(!t||t.status!=="ACTIVE"))return{node:n,rootNode:t}}abandonToolTimings(e){for(const[,n]of e.toolTimings){const{toolName:t,startTimes:i}=n;for(const s of i)this.safeExecute(()=>{e.interaction.addToolExecution({id:d(),type:"tool",name:t,latency_ms:String(Math.min(performance.now()-s,this.maxIdleTimeMs)),error:"tool call abandoned \u2014 interaction closed"})},`abandoning tool execution ${t}`)}e.toolTimings.clear()}tryFlushPendingClose(e){if(e.toolTimings.size!==0||e.pendingClose===void 0||e.parentSessionKey===void 0)return!1;const{error:n}=e.pendingClose;return e.pendingClose=void 0,this.abandonToolTimings(e),this.safeExecute(()=>{e.interaction.finish(n)},"tryFlushPendingClose"),this.cleanupNodeMemory(e),!0}tryFlushPendingFinish(e){if(e.toolTimings.size!==0||e.pendingFinish===void 0)return!1;const n=e.pendingFinish;return e.pendingFinish=void 0,this.safeExecute(()=>{e.interaction.finish(n)},"tryFlushPendingFinish"),this.removeNode(e.sessionKey),!0}closeRoot(e,n){const t=this.nodes.get(e);if(!t||t.status!=="ACTIVE")return;this.logger.warn(`[atheon-openclaw] closing active interaction \u2014 ${n}`);const i=[],s=[t];for(;s.length>0;){const o=s.pop();if(o.status==="ACTIVE"){i.push(o);for(const r of o.childSessionKeys){const a=this.nodes.get(r);a&&a.status==="ACTIVE"&&s.push(a)}}}for(let o=i.length-1;o>=0;o--){const r=i[o];this.abandonToolTimings(r),this.safeExecute(()=>{r.parentSessionKey===void 0?r.interaction.finish({output:r.outputText}):r.interaction.finish("subagent abandoned \u2014 parent interaction closed")},"closeRoot node finish"),r.status="RESOLVED"}for(const o of i)this.cleanupNodeMemory(o)}closeChild(e,n){const t=this.nodes.get(e);if(!(!t||t.parentSessionKey===void 0)&&t.status==="ACTIVE"){if(t.toolTimings.size>0){if(t.pendingClose!==void 0){this.logger.warn(`[atheon-openclaw] closeChild called twice while tools in-flight (childSessionKey=${e}) \u2014 ignoring duplicate close`);return}t.pendingClose={error:n};return}this.abandonToolTimings(t),this.safeExecute(()=>{t.interaction.finish(n)},"closeChild finish"),this.cleanupNodeMemory(t)}}reapStaleNodes(e){const n=[],t=[];for(const i of this.nodes.values()){const s=e-i.lastActivityAt>this.maxIdleTimeMs;i.parentSessionKey===void 0?s&&n.push(i.sessionKey):(!this.nodes.has(i.parentSessionKey)||s)&&t.push(i.sessionKey)}for(const i of n)this.closeRoot(i,"timeout \u2014 session orphaned");for(const i of t){this.logger.warn(`[atheon-openclaw] reaping orphaned child interaction \u2014 ${i}`);const s=this.nodes.get(i);s&&s.status==="ACTIVE"&&(this.abandonToolTimings(s),this.safeExecute(()=>{s.interaction.finish("timeout or orphaned")},"reapStaleNodes child finish"),this.cleanupNodeMemory(s))}}}export{h as SessionRegistry};
|
|
2
|
+
//# sourceMappingURL=session_registry.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/session_registry.ts"],
|
|
4
|
+
"sourcesContent": ["import { randomUUID } from \"node:crypto\";\nimport type { OpenClawPluginApi } from \"openclaw/plugin-sdk/core\";\nimport type { Interaction, ChildInteraction } from \"@atheon-inc/codex\";\nimport { formatError } from \"./utils.js\";\n\nexport type NodeStatus = \"ACTIVE\" | \"RESOLVED\" | \"CLOSED\";\n\nexport interface PendingFinish {\n output: string;\n finishReason: string;\n tokensInput?: number;\n tokensOutput?: number;\n}\n\nexport interface PendingClose {\n error?: string;\n}\n\nexport interface ToolTimingEntry {\n toolName: string;\n startTimes: number[];\n}\n\nexport interface InteractionNode {\n readonly sessionKey: string;\n readonly parentSessionKey: string | undefined;\n\n status: NodeStatus;\n\n interaction: Interaction | ChildInteraction;\n\n outputText: string | null | undefined;\n\n pendingFinish?: PendingFinish;\n pendingClose?: PendingClose;\n\n childSessionKeys: Set<string>;\n linkedAgentIds: Set<string>;\n toolTimings: Map<string, ToolTimingEntry>;\n lastActivityAt: number;\n}\n\nexport class SessionRegistry {\n private readonly nodes = new Map<string, InteractionNode>();\n private readonly agentToSession = new Map<string, string>();\n\n private readonly maxIdleTimeMs: number;\n\n constructor(\n private readonly logger: OpenClawPluginApi[\"logger\"],\n opts?: { maxIdleTimeMs?: number },\n ) {\n this.maxIdleTimeMs = opts?.maxIdleTimeMs ?? 60 * 60 * 1000;\n }\n\n private cleanupNodeMemory(node: InteractionNode): void {\n if (node.parentSessionKey !== undefined) {\n const parent = this.nodes.get(node.parentSessionKey);\n if (parent) parent.childSessionKeys.delete(node.sessionKey);\n }\n for (const agentId of node.linkedAgentIds) {\n this.agentToSession.delete(agentId);\n }\n this.nodes.delete(node.sessionKey);\n node.status = \"CLOSED\";\n node.linkedAgentIds.clear();\n node.childSessionKeys.clear();\n node.toolTimings.clear();\n }\n\n private safeExecute(fn: () => void, context: string): void {\n try {\n fn();\n } catch (err) {\n this.logger.warn(\n `[atheon-openclaw] ${context} failed: ${formatError(err)}`,\n );\n }\n }\n\n createRootNode(\n sessionKey: string,\n interaction: Interaction,\n ): InteractionNode {\n const existing = this.nodes.get(sessionKey);\n if (existing) {\n this.logger.warn(\n `[atheon-openclaw] createRootNode called for existing sessionKey=${sessionKey} \u2014 closing existing node first`,\n );\n this.closeRoot(sessionKey, \"replaced by new root node\");\n }\n\n const node: InteractionNode = {\n sessionKey,\n parentSessionKey: undefined,\n status: \"ACTIVE\",\n interaction,\n outputText: \"\",\n childSessionKeys: new Set(),\n linkedAgentIds: new Set(),\n toolTimings: new Map(),\n lastActivityAt: Date.now(),\n };\n this.nodes.set(sessionKey, node);\n return node;\n }\n\n createChildNode(\n childSessionKey: string,\n parentSessionKey: string,\n interaction: ChildInteraction,\n ): InteractionNode | undefined {\n const parent = this.nodes.get(parentSessionKey);\n if (!parent) return undefined;\n\n const existing = this.nodes.get(childSessionKey);\n if (existing) {\n this.closeChild(childSessionKey, \"subagent reset \u2014 new spawn initiated\");\n }\n\n const node: InteractionNode = {\n sessionKey: childSessionKey,\n parentSessionKey,\n status: \"ACTIVE\",\n interaction,\n outputText: \"\",\n childSessionKeys: new Set(),\n linkedAgentIds: new Set(),\n toolTimings: new Map(),\n lastActivityAt: Date.now(),\n };\n\n this.nodes.set(childSessionKey, node);\n parent.childSessionKeys.add(childSessionKey);\n parent.lastActivityAt = Date.now();\n return node;\n }\n\n getNode(sessionKey: string): InteractionNode | undefined {\n return this.nodes.get(sessionKey);\n }\n\n removeNode(sessionKey: string): void {\n const node = this.nodes.get(sessionKey);\n if (node) this.cleanupNodeMemory(node);\n }\n\n linkAgent(sessionKey: string, agentId: unknown): void {\n if (typeof agentId !== \"string\" || agentId.length === 0) return;\n const node = this.nodes.get(sessionKey);\n if (!node) return;\n node.linkedAgentIds.add(agentId);\n this.agentToSession.set(agentId, sessionKey);\n node.lastActivityAt = Date.now();\n }\n\n resolveSessionKey(rawKey: string): string | undefined {\n if (this.nodes.has(rawKey)) return rawKey;\n return this.agentToSession.get(rawKey);\n }\n\n resolveToolTarget(\n sessionKey: string,\n ): { node: InteractionNode; rootNode: InteractionNode } | undefined {\n const node = this.nodes.get(sessionKey);\n if (!node || node.status !== \"ACTIVE\") return undefined;\n\n if (node.parentSessionKey === undefined) {\n return { node, rootNode: node };\n }\n\n const rootNode = this.nodes.get(node.parentSessionKey);\n if (!rootNode || rootNode.status !== \"ACTIVE\") return undefined;\n\n return { node, rootNode };\n }\n\n private abandonToolTimings(node: InteractionNode): void {\n for (const [, entry] of node.toolTimings) {\n const { toolName, startTimes } = entry;\n for (const startTime of startTimes) {\n this.safeExecute(() => {\n node.interaction.addToolExecution({\n id: randomUUID(),\n type: \"tool\",\n name: toolName,\n latency_ms: String(\n Math.min(performance.now() - startTime, this.maxIdleTimeMs),\n ),\n error: \"tool call abandoned \u2014 interaction closed\",\n });\n }, `abandoning tool execution ${toolName}`);\n }\n }\n node.toolTimings.clear();\n }\n\n tryFlushPendingClose(node: InteractionNode): boolean {\n if (\n node.toolTimings.size !== 0 ||\n node.pendingClose === undefined ||\n node.parentSessionKey === undefined\n ) {\n return false;\n }\n\n const { error } = node.pendingClose;\n node.pendingClose = undefined;\n\n this.abandonToolTimings(node);\n\n this.safeExecute(() => {\n (node.interaction as ChildInteraction).finish(error);\n }, \"tryFlushPendingClose\");\n\n this.cleanupNodeMemory(node);\n return true;\n }\n\n tryFlushPendingFinish(rootNode: InteractionNode): boolean {\n if (\n rootNode.toolTimings.size !== 0 ||\n rootNode.pendingFinish === undefined\n ) {\n return false;\n }\n\n const finishArgs = rootNode.pendingFinish;\n rootNode.pendingFinish = undefined;\n\n this.safeExecute(() => {\n (rootNode.interaction as Interaction).finish(finishArgs);\n }, \"tryFlushPendingFinish\");\n\n this.removeNode(rootNode.sessionKey);\n return true;\n }\n\n closeRoot(sessionKey: string, reason: string): void {\n const rootNode = this.nodes.get(sessionKey);\n if (!rootNode || rootNode.status !== \"ACTIVE\") return;\n\n this.logger.warn(\n `[atheon-openclaw] closing active interaction \u2014 ${reason}`,\n );\n\n const postOrder: InteractionNode[] = [];\n const stack: InteractionNode[] = [rootNode];\n\n while (stack.length > 0) {\n const current = stack.pop()!;\n if (current.status !== \"ACTIVE\") continue;\n postOrder.push(current);\n for (const childKey of current.childSessionKeys) {\n const child = this.nodes.get(childKey);\n if (child && child.status === \"ACTIVE\") stack.push(child);\n }\n }\n\n for (let idx = postOrder.length - 1; idx >= 0; idx--) {\n const node = postOrder[idx];\n this.abandonToolTimings(node);\n\n this.safeExecute(() => {\n if (node.parentSessionKey === undefined) {\n (node.interaction as Interaction).finish({ output: node.outputText });\n } else {\n (node.interaction as ChildInteraction).finish(\n \"subagent abandoned \u2014 parent interaction closed\",\n );\n }\n }, \"closeRoot node finish\");\n\n node.status = \"RESOLVED\";\n }\n\n for (const node of postOrder) {\n this.cleanupNodeMemory(node);\n }\n }\n\n closeChild(childSessionKey: string, error?: string): void {\n const node = this.nodes.get(childSessionKey);\n if (!node || node.parentSessionKey === undefined) return;\n if (node.status !== \"ACTIVE\") return;\n\n if (node.toolTimings.size > 0) {\n if (node.pendingClose !== undefined) {\n this.logger.warn(\n `[atheon-openclaw] closeChild called twice while tools in-flight (childSessionKey=${childSessionKey}) \u2014 ignoring duplicate close`,\n );\n return;\n }\n node.pendingClose = { error };\n return;\n }\n\n this.abandonToolTimings(node);\n\n this.safeExecute(() => {\n (node.interaction as ChildInteraction).finish(error);\n }, \"closeChild finish\");\n\n this.cleanupNodeMemory(node);\n }\n\n reapStaleNodes(now: number): void {\n const staleRootKeys: string[] = [];\n const staleOrOrphanedChildKeys: string[] = [];\n\n for (const node of this.nodes.values()) {\n const isStale = now - node.lastActivityAt > this.maxIdleTimeMs;\n\n if (node.parentSessionKey === undefined) {\n if (isStale) staleRootKeys.push(node.sessionKey);\n } else {\n if (!this.nodes.has(node.parentSessionKey) || isStale) {\n staleOrOrphanedChildKeys.push(node.sessionKey);\n }\n }\n }\n\n for (const key of staleRootKeys) {\n this.closeRoot(key, \"timeout \u2014 session orphaned\");\n }\n\n for (const key of staleOrOrphanedChildKeys) {\n this.logger.warn(\n `[atheon-openclaw] reaping orphaned child interaction \u2014 ${key}`,\n );\n\n const node = this.nodes.get(key);\n if (node && node.status === \"ACTIVE\") {\n this.abandonToolTimings(node);\n this.safeExecute(() => {\n (node.interaction as ChildInteraction).finish(\"timeout or orphaned\");\n }, \"reapStaleNodes child finish\");\n this.cleanupNodeMemory(node);\n }\n }\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["randomUUID", "formatError", "SessionRegistry", "logger", "opts", "node", "parent", "agentId", "fn", "context", "err", "sessionKey", "interaction", "childSessionKey", "parentSessionKey", "rawKey", "rootNode", "entry", "toolName", "startTimes", "startTime", "error", "finishArgs", "reason", "postOrder", "stack", "current", "childKey", "child", "idx", "now", "staleRootKeys", "staleOrOrphanedChildKeys", "isStale", "key"]
|
|
7
|
+
}
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
function o(n){if(n instanceof Error)return n.stack??n.message;if(typeof n=="string")return n;try{return JSON.stringify(n)}catch{return String(n)}}function r(n){if(typeof n!="string")return;const e=n.toLowerCase();return e.includes("anthropic")?"anthropic":e.includes("openai")?"openai":e.includes("google")||e.includes("gemini")?"google":e.includes("mistral")?"mistral":e.includes("cohere")?"cohere":e}function i(n){const e=n.channel??n.channelId??n.channelType;return typeof e=="string"&&e.length>0?e:void 0}function u(n,e){const t=n.toolCallId??e.toolCallId;return typeof t=="string"&&t.length>0?t:void 0}function s(n){const e=n.trigger??n.triggerType;return typeof e=="string"&&e.length>0?e:void 0}function d(n,e,t){return`session:${n}:tool:${e}:${t??"no-id-fallback"}`}function l(n){return typeof n=="string"&&n.length>0?n:void 0}function f(n){const e=n!=null&&typeof n=="object"?n:{},t=typeof e.apiKey=="string"?e.apiKey:process.env.ATHEON_API_KEY??"";if(!t)throw new Error("[atheon-openclaw] 'apiKey' is required. Set it in the plugin config or via ATHEON_API_KEY.");return{apiKey:t,baseUrl:typeof e.baseUrl=="string"?e.baseUrl:void 0,uploadSize:typeof e.uploadSize=="number"?e.uploadSize:void 0,uploadInterval:typeof e.uploadInterval=="number"?e.uploadInterval:void 0}}export{d as buildSpanKey,o as formatError,l as nonEmptyString,r as normalizeProvider,f as parseRawConfigIntoAtheonCodexClientOptions,i as resolveChannelId,u as resolveToolCallId,s as resolveTrigger};
|
|
2
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/utils.ts"],
|
|
4
|
+
"sourcesContent": ["import type { AtheonCodexClientOptions } from \"@atheon-inc/codex\";\n\nexport function formatError(err: unknown): string {\n if (err instanceof Error) {\n return err.stack ?? err.message;\n }\n if (typeof err === \"string\") {\n return err;\n }\n try {\n return JSON.stringify(err);\n } catch {\n return String(err);\n }\n}\n\nexport function normalizeProvider(provider: unknown): string | undefined {\n if (typeof provider !== \"string\") return undefined;\n const p = provider.toLowerCase();\n if (p.includes(\"anthropic\")) return \"anthropic\";\n if (p.includes(\"openai\")) return \"openai\";\n if (p.includes(\"google\") || p.includes(\"gemini\")) return \"google\";\n if (p.includes(\"mistral\")) return \"mistral\";\n if (p.includes(\"cohere\")) return \"cohere\";\n return p;\n}\n\nexport function resolveChannelId(\n ctx: Record<string, unknown>,\n): string | undefined {\n const channel = ctx.channel ?? ctx.channelId ?? ctx.channelType;\n return typeof channel === \"string\" && channel.length > 0\n ? channel\n : undefined;\n}\n\nexport function resolveToolCallId(\n event: Record<string, unknown>,\n ctx: Record<string, unknown>,\n): string | undefined {\n const id = event.toolCallId ?? ctx.toolCallId;\n return typeof id === \"string\" && id.length > 0 ? id : undefined;\n}\n\nexport function resolveTrigger(\n ctx: Record<string, unknown>,\n): string | undefined {\n const trigger = ctx.trigger ?? ctx.triggerType;\n return typeof trigger === \"string\" && trigger.length > 0\n ? trigger\n : undefined;\n}\n\nexport function buildSpanKey(\n sessionKey: string,\n toolName: string,\n toolCallId?: string,\n): string {\n return `session:${sessionKey}:tool:${toolName}:${toolCallId ?? \"no-id-fallback\"}`;\n}\n\nexport function nonEmptyString(value: unknown): string | undefined {\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\nexport function parseRawConfigIntoAtheonCodexClientOptions(\n raw: unknown,\n): AtheonCodexClientOptions {\n const obj =\n raw != null && typeof raw === \"object\"\n ? (raw as Record<string, unknown>)\n : {};\n\n const apiKey =\n typeof obj.apiKey === \"string\"\n ? obj.apiKey\n : (process.env.ATHEON_API_KEY ?? \"\");\n if (!apiKey) {\n throw new Error(\n \"[atheon-openclaw] 'apiKey' is required. Set it in the plugin config or via ATHEON_API_KEY.\",\n );\n }\n\n return {\n apiKey,\n baseUrl: typeof obj.baseUrl === \"string\" ? obj.baseUrl : undefined,\n uploadSize: typeof obj.uploadSize === \"number\" ? obj.uploadSize : undefined,\n uploadInterval:\n typeof obj.uploadInterval === \"number\" ? obj.uploadInterval : undefined,\n };\n}\n"],
|
|
5
|
+
"mappings": "AAEO,SAASA,EAAYC,EAAsB,CAChD,GAAIA,aAAe,MACjB,OAAOA,EAAI,OAASA,EAAI,QAE1B,GAAI,OAAOA,GAAQ,SACjB,OAAOA,EAET,GAAI,CACF,OAAO,KAAK,UAAUA,CAAG,CAC3B,MAAQ,CACN,OAAO,OAAOA,CAAG,CACnB,CACF,CAEO,SAASC,EAAkBC,EAAuC,CACvE,GAAI,OAAOA,GAAa,SAAU,OAClC,MAAMC,EAAID,EAAS,YAAY,EAC/B,OAAIC,EAAE,SAAS,WAAW,EAAU,YAChCA,EAAE,SAAS,QAAQ,EAAU,SAC7BA,EAAE,SAAS,QAAQ,GAAKA,EAAE,SAAS,QAAQ,EAAU,SACrDA,EAAE,SAAS,SAAS,EAAU,UAC9BA,EAAE,SAAS,QAAQ,EAAU,SAC1BA,CACT,CAEO,SAASC,EACdC,EACoB,CACpB,MAAMC,EAAUD,EAAI,SAAWA,EAAI,WAAaA,EAAI,YACpD,OAAO,OAAOC,GAAY,UAAYA,EAAQ,OAAS,EACnDA,EACA,MACN,CAEO,SAASC,EACdC,EACAH,EACoB,CACpB,MAAMI,EAAKD,EAAM,YAAcH,EAAI,WACnC,OAAO,OAAOI,GAAO,UAAYA,EAAG,OAAS,EAAIA,EAAK,MACxD,CAEO,SAASC,EACdL,EACoB,CACpB,MAAMM,EAAUN,EAAI,SAAWA,EAAI,YACnC,OAAO,OAAOM,GAAY,UAAYA,EAAQ,OAAS,EACnDA,EACA,MACN,CAEO,SAASC,EACdC,EACAC,EACAC,EACQ,CACR,MAAO,WAAWF,CAAU,SAASC,CAAQ,IAAIC,GAAc,gBAAgB,EACjF,CAEO,SAASC,EAAeC,EAAoC,CACjE,OAAO,OAAOA,GAAU,UAAYA,EAAM,OAAS,EAAIA,EAAQ,MACjE,CAEO,SAASC,EACdC,EAC0B,CAC1B,MAAMC,EACJD,GAAO,MAAQ,OAAOA,GAAQ,SACzBA,EACD,CAAC,EAEDE,EACJ,OAAOD,EAAI,QAAW,SAClBA,EAAI,OACH,QAAQ,IAAI,gBAAkB,GACrC,GAAI,CAACC,EACH,MAAM,IAAI,MACR,4FACF,EAGF,MAAO,CACL,OAAAA,EACA,QAAS,OAAOD,EAAI,SAAY,SAAWA,EAAI,QAAU,OACzD,WAAY,OAAOA,EAAI,YAAe,SAAWA,EAAI,WAAa,OAClE,eACE,OAAOA,EAAI,gBAAmB,SAAWA,EAAI,eAAiB,MAClE,CACF",
|
|
6
|
+
"names": ["formatError", "err", "normalizeProvider", "provider", "p", "resolveChannelId", "ctx", "channel", "resolveToolCallId", "event", "id", "resolveTrigger", "trigger", "buildSpanKey", "sessionKey", "toolName", "toolCallId", "nonEmptyString", "value", "parseRawConfigIntoAtheonCodexClientOptions", "raw", "obj", "apiKey"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "atheon-openclaw",
|
|
3
|
+
"name": "atheon",
|
|
4
|
+
"description": "Export LLM, Tools and SubAgent analytics to Atheon using atheon-codex",
|
|
5
|
+
"kind": "feature",
|
|
6
|
+
"configSchema": {
|
|
7
|
+
"type": "object",
|
|
8
|
+
"properties": {
|
|
9
|
+
"apiKey": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"description": "Your Atheon Project API Key"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"required": [
|
|
15
|
+
"apiKey"
|
|
16
|
+
]
|
|
17
|
+
},
|
|
18
|
+
"uiHints": {
|
|
19
|
+
"apiKey": {
|
|
20
|
+
"label": "Atheon API Key",
|
|
21
|
+
"sensitive": true
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
package/package.json
CHANGED
|
@@ -1,52 +1,53 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
2
|
+
"name": "@atheon-inc/openclaw-plugin",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Atheon OpenClaw analytics plugin",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/atheon-inc/atheon-openclaw-plugin.git"
|
|
11
|
+
},
|
|
12
|
+
"type": "module",
|
|
13
|
+
"main": "./dist/index.js",
|
|
14
|
+
"files": [
|
|
15
|
+
"dist/",
|
|
16
|
+
"openclaw.plugin.json",
|
|
17
|
+
"README.md",
|
|
18
|
+
"LICENSE"
|
|
19
|
+
],
|
|
20
|
+
"openclaw": {
|
|
21
|
+
"extensions": [
|
|
22
|
+
"./dist/index.js"
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "node build.mjs",
|
|
27
|
+
"prepublishOnly": "npm run build"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@atheon-inc/codex": "^1.0"
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"openclaw": ">=2026.3"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/node": "^25.5",
|
|
37
|
+
"esbuild": "^0.27",
|
|
38
|
+
"typescript": "^6.0"
|
|
39
|
+
},
|
|
40
|
+
"versioning": {
|
|
14
41
|
"files": [
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
"prepublishOnly": "npm run build"
|
|
27
|
-
},
|
|
28
|
-
"dependencies": {
|
|
29
|
-
"@atheon-inc/codex": "^1.0"
|
|
30
|
-
},
|
|
31
|
-
"peerDependencies": {
|
|
32
|
-
"openclaw": ">=2026.3"
|
|
33
|
-
},
|
|
34
|
-
"devDependencies": {
|
|
35
|
-
"@types/node": "^25.5",
|
|
36
|
-
"esbuild": "^0.27",
|
|
37
|
-
"typescript": "^6.0"
|
|
38
|
-
},
|
|
39
|
-
"versioning": {
|
|
40
|
-
"files": [
|
|
41
|
-
"src/index.ts:__version__"
|
|
42
|
-
]
|
|
43
|
-
},
|
|
44
|
-
"keywords": [
|
|
45
|
-
"atheon",
|
|
46
|
-
"openclaw",
|
|
47
|
-
"plugin",
|
|
48
|
-
"analytics"
|
|
49
|
-
],
|
|
50
|
-
"author": "Saurabh Ghanekar <saurabh@atheon.ad>",
|
|
51
|
-
"license": "Apache-2.0"
|
|
42
|
+
"src/index.ts:__version__"
|
|
43
|
+
]
|
|
44
|
+
},
|
|
45
|
+
"keywords": [
|
|
46
|
+
"atheon",
|
|
47
|
+
"openclaw",
|
|
48
|
+
"plugin",
|
|
49
|
+
"analytics"
|
|
50
|
+
],
|
|
51
|
+
"author": "Saurabh Ghanekar <saurabh@atheon.ad>",
|
|
52
|
+
"license": "Apache-2.0"
|
|
52
53
|
}
|
/package/{License.md → LICENSE}
RENAMED
|
File without changes
|