@actagent/acpx 2026.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +54 -0
- package/README.md +33 -0
- package/actagent.plugin.json +174 -0
- package/index.test.ts +120 -0
- package/index.ts +23 -0
- package/npm-shrinkwrap.json +2246 -0
- package/package.json +50 -0
- package/register.runtime.test.ts +168 -0
- package/register.runtime.ts +108 -0
- package/runtime-api.ts +53 -0
- package/setup-api.ts +22 -0
- package/skills/acp-router/SKILL.md +245 -0
- package/src/claude-agent-acp-completion.test.ts +167 -0
- package/src/codex-auth-bridge.test.ts +799 -0
- package/src/codex-auth-bridge.ts +764 -0
- package/src/codex-trust-config.ts +304 -0
- package/src/command-line.ts +56 -0
- package/src/config-schema.ts +130 -0
- package/src/config.test.ts +296 -0
- package/src/config.ts +290 -0
- package/src/manifest.test.ts +22 -0
- package/src/process-lease.test.ts +90 -0
- package/src/process-lease.ts +194 -0
- package/src/process-reaper.test.ts +337 -0
- package/src/process-reaper.ts +427 -0
- package/src/runtime-internals/mcp-command-line.mjs +128 -0
- package/src/runtime-internals/mcp-command-line.test.ts +60 -0
- package/src/runtime-internals/mcp-proxy.mjs +132 -0
- package/src/runtime-internals/mcp-proxy.test.ts +131 -0
- package/src/runtime-proxy.ts +47 -0
- package/src/runtime-turn.ts +200 -0
- package/src/runtime.test.ts +1830 -0
- package/src/runtime.ts +1254 -0
- package/src/service.test.ts +839 -0
- package/src/service.ts +439 -0
- package/tsconfig.json +16 -0
package/AGENTS.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# ACPX Extension Notes
|
|
2
|
+
|
|
3
|
+
This file applies to work under `extensions/acpx/`.
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
The ACPX extension is a thin ACTAgent wrapper around the published `acpx` package. Keep reusable ACP runtime logic in `actagent/acpx`, not in this extension.
|
|
8
|
+
|
|
9
|
+
## Default Version Policy
|
|
10
|
+
|
|
11
|
+
- `extensions/acpx/package.json` should point at a published npm release by default.
|
|
12
|
+
- Do not leave the extension pinned to a temporary GitHub commit or local checkout once the ACPX release exists.
|
|
13
|
+
- Do not leave temporary pnpm build-script allowlist exceptions behind after switching back to a published ACPX package.
|
|
14
|
+
|
|
15
|
+
## Unreleased ACPX Development Flow
|
|
16
|
+
|
|
17
|
+
Use this flow when ACTAgent needs unreleased ACPX changes before the ACPX version is published.
|
|
18
|
+
|
|
19
|
+
1. Make the ACPX code change in the `actagent/acpx` repo first.
|
|
20
|
+
2. In ACTAgent, temporarily point `extensions/acpx/package.json` at the ACPX GitHub commit you need.
|
|
21
|
+
3. If pnpm blocks ACPX lifecycle/build scripts for that temporary GitHub-sourced package, temporarily add `acpx: true` to `allowBuilds` in `pnpm-workspace.yaml`.
|
|
22
|
+
4. Refresh the root workspace lock:
|
|
23
|
+
- `pnpm install --lockfile-only --filter ./extensions/acpx`
|
|
24
|
+
5. Refresh the extension-local npm lock for install metadata:
|
|
25
|
+
- `cd extensions/acpx && npm install --package-lock-only --ignore-scripts`
|
|
26
|
+
6. Rebuild ACTAgent and restart the gateway before doing live ACP validation.
|
|
27
|
+
7. Once ACPX is released, switch `extensions/acpx/package.json` back to the published npm version and refresh the same lockfiles again.
|
|
28
|
+
8. Remove any temporary `acpx` build-script allowlist entry that was only needed for the GitHub-sourced development pin.
|
|
29
|
+
|
|
30
|
+
## Lockfile Notes
|
|
31
|
+
|
|
32
|
+
- `pnpm-lock.yaml` is the tracked workspace lockfile and must match the ACPX version referenced by `extensions/acpx/package.json`.
|
|
33
|
+
- `extensions/acpx/package-lock.json` is useful local install metadata for the plugin package.
|
|
34
|
+
- If `extensions/acpx/package-lock.json` is gitignored in this repo state, regenerating it is still useful for local verification, but it will not appear in `git status`.
|
|
35
|
+
|
|
36
|
+
## Local Runtime Validation
|
|
37
|
+
|
|
38
|
+
When ACPX integration changes here, prefer this sequence:
|
|
39
|
+
|
|
40
|
+
1. `pnpm install --filter ./extensions/acpx`
|
|
41
|
+
2. `pnpm test:extension acpx`
|
|
42
|
+
3. `pnpm build`
|
|
43
|
+
4. Restart the local gateway if ACP runtime behavior or bundled plugin wiring changed.
|
|
44
|
+
5. If the change affects direct ACP behavior in chat, run a real ACP smoke after restart.
|
|
45
|
+
|
|
46
|
+
## Direct ACPX Binary Policy
|
|
47
|
+
|
|
48
|
+
- Prefer the plugin-local ACPX binary under `extensions/acpx/node_modules/.bin/acpx`.
|
|
49
|
+
- Do not rely on a globally installed `acpx` binary for ACTAgent ACP validation.
|
|
50
|
+
- If the plugin-local ACPX binary is missing or on the wrong version, reinstall it from the version pinned in `extensions/acpx/package.json`.
|
|
51
|
+
|
|
52
|
+
## Boundary Rule
|
|
53
|
+
|
|
54
|
+
If a change feels like shared ACP runtime behavior instead of ACTAgent-specific glue, move it to `actagent/acpx` and consume it from here instead of re-implementing it inside `extensions/acpx`.
|
package/README.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# @actagent/acpx
|
|
2
|
+
|
|
3
|
+
Official ACP runtime backend for ACTAgent.
|
|
4
|
+
|
|
5
|
+
ACPx lets ACTAgent run external coding harnesses through the Agent Client Protocol while ACTAgent still owns sessions, channels, delivery, permissions, and Gateway state.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
actagent plugins install @actagent/acpx
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Restart the Gateway after installing or updating the plugin.
|
|
14
|
+
|
|
15
|
+
## What it provides
|
|
16
|
+
|
|
17
|
+
- ACP-backed agent runtime sessions.
|
|
18
|
+
- Plugin-owned session and transport management.
|
|
19
|
+
- MCP bridge helpers for ACTAgent tools and plugin tools.
|
|
20
|
+
- Static runtime assets used by the ACP process bridge.
|
|
21
|
+
|
|
22
|
+
## Configure
|
|
23
|
+
|
|
24
|
+
Use the ACP docs for harness-specific setup, permission modes, and model/runtime selection:
|
|
25
|
+
|
|
26
|
+
- https://docs.actagent.ai/tools/acp-agents-setup
|
|
27
|
+
- https://docs.actagent.ai/tools/acp-agents
|
|
28
|
+
|
|
29
|
+
## Package
|
|
30
|
+
|
|
31
|
+
- Plugin id: `acpx`
|
|
32
|
+
- Package: `@actagent/acpx`
|
|
33
|
+
- Minimum ACTAgent host: `2026.4.25`
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "acpx",
|
|
3
|
+
"activation": {
|
|
4
|
+
"onStartup": true
|
|
5
|
+
},
|
|
6
|
+
"enabledByDefault": true,
|
|
7
|
+
"name": "ACPX Runtime",
|
|
8
|
+
"description": "ACTAgent ACP runtime backend with plugin-owned session and transport management.",
|
|
9
|
+
"skills": ["./skills"],
|
|
10
|
+
"configSchema": {
|
|
11
|
+
"type": "object",
|
|
12
|
+
"additionalProperties": false,
|
|
13
|
+
"properties": {
|
|
14
|
+
"cwd": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"minLength": 1
|
|
17
|
+
},
|
|
18
|
+
"stateDir": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"minLength": 1
|
|
21
|
+
},
|
|
22
|
+
"probeAgent": {
|
|
23
|
+
"type": "string",
|
|
24
|
+
"minLength": 1
|
|
25
|
+
},
|
|
26
|
+
"permissionMode": {
|
|
27
|
+
"type": "string",
|
|
28
|
+
"enum": ["approve-all", "approve-reads", "deny-all"]
|
|
29
|
+
},
|
|
30
|
+
"nonInteractivePermissions": {
|
|
31
|
+
"type": "string",
|
|
32
|
+
"enum": ["deny", "fail"]
|
|
33
|
+
},
|
|
34
|
+
"pluginToolsMcpBridge": {
|
|
35
|
+
"type": "boolean"
|
|
36
|
+
},
|
|
37
|
+
"actAgentToolsMcpBridge": {
|
|
38
|
+
"type": "boolean"
|
|
39
|
+
},
|
|
40
|
+
"strictWindowsCmdWrapper": {
|
|
41
|
+
"type": "boolean"
|
|
42
|
+
},
|
|
43
|
+
"timeoutSeconds": {
|
|
44
|
+
"type": "number",
|
|
45
|
+
"minimum": 0.001,
|
|
46
|
+
"default": 120
|
|
47
|
+
},
|
|
48
|
+
"queueOwnerTtlSeconds": {
|
|
49
|
+
"type": "number",
|
|
50
|
+
"minimum": 0
|
|
51
|
+
},
|
|
52
|
+
"probeAgent": {
|
|
53
|
+
"type": "string",
|
|
54
|
+
"minLength": 1
|
|
55
|
+
},
|
|
56
|
+
"mcpServers": {
|
|
57
|
+
"type": "object",
|
|
58
|
+
"additionalProperties": {
|
|
59
|
+
"type": "object",
|
|
60
|
+
"properties": {
|
|
61
|
+
"command": {
|
|
62
|
+
"type": "string",
|
|
63
|
+
"minLength": 1,
|
|
64
|
+
"description": "Command to run the MCP server"
|
|
65
|
+
},
|
|
66
|
+
"args": {
|
|
67
|
+
"type": "array",
|
|
68
|
+
"items": { "type": "string" },
|
|
69
|
+
"description": "Arguments to pass to the command"
|
|
70
|
+
},
|
|
71
|
+
"env": {
|
|
72
|
+
"type": "object",
|
|
73
|
+
"additionalProperties": { "type": "string" },
|
|
74
|
+
"description": "Environment variables for the MCP server"
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
"required": ["command"]
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
"agents": {
|
|
81
|
+
"type": "object",
|
|
82
|
+
"additionalProperties": {
|
|
83
|
+
"type": "object",
|
|
84
|
+
"properties": {
|
|
85
|
+
"command": {
|
|
86
|
+
"type": "string",
|
|
87
|
+
"minLength": 1
|
|
88
|
+
},
|
|
89
|
+
"args": {
|
|
90
|
+
"type": "array",
|
|
91
|
+
"items": { "type": "string" }
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
"required": ["command"]
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
"uiHints": {
|
|
100
|
+
"cwd": {
|
|
101
|
+
"label": "Default Working Directory",
|
|
102
|
+
"help": "Default working directory for embedded ACP session operations when not set per session."
|
|
103
|
+
},
|
|
104
|
+
"stateDir": {
|
|
105
|
+
"label": "State Directory",
|
|
106
|
+
"help": "Directory used for embedded ACP session state and persistence."
|
|
107
|
+
},
|
|
108
|
+
"permissionMode": {
|
|
109
|
+
"label": "Permission Mode",
|
|
110
|
+
"help": "Default permission policy for embedded ACP runtime prompts."
|
|
111
|
+
},
|
|
112
|
+
"nonInteractivePermissions": {
|
|
113
|
+
"label": "Non-Interactive Permission Policy",
|
|
114
|
+
"help": "Policy when interactive permission prompts are unavailable."
|
|
115
|
+
},
|
|
116
|
+
"pluginToolsMcpBridge": {
|
|
117
|
+
"label": "Plugin Tools MCP Bridge",
|
|
118
|
+
"help": "Default off. When enabled, inject the built-in ACTAgent plugin-tools MCP server into embedded ACP sessions so ACP agents can call plugin-registered tools.",
|
|
119
|
+
"advanced": true
|
|
120
|
+
},
|
|
121
|
+
"actAgentToolsMcpBridge": {
|
|
122
|
+
"label": "ACTAgent Tools MCP Bridge",
|
|
123
|
+
"help": "Default off. When enabled, inject the built-in ACTAgent core-tools MCP server into embedded ACP sessions so ACP agents can call selected built-in tools such as cron.",
|
|
124
|
+
"advanced": true
|
|
125
|
+
},
|
|
126
|
+
"strictWindowsCmdWrapper": {
|
|
127
|
+
"label": "Strict Windows cmd Wrapper",
|
|
128
|
+
"help": "Legacy compatibility field. The current embedded acpx/runtime package uses its own Windows command resolution behavior. Setting this to false is accepted for compatibility and logged as ignored.",
|
|
129
|
+
"advanced": true
|
|
130
|
+
},
|
|
131
|
+
"timeoutSeconds": {
|
|
132
|
+
"label": "Runtime Operation Timeout Seconds",
|
|
133
|
+
"help": "Timeout for embedded ACP runtime startup and control operations. ACP turns use ACTAgent agent/run timeouts.",
|
|
134
|
+
"advanced": true
|
|
135
|
+
},
|
|
136
|
+
"queueOwnerTtlSeconds": {
|
|
137
|
+
"label": "Queue Owner TTL Seconds",
|
|
138
|
+
"help": "Reserved compatibility field for the older embedded ACPX queue-owner path. Accepted for compatibility and logged as ignored.",
|
|
139
|
+
"advanced": true
|
|
140
|
+
},
|
|
141
|
+
"probeAgent": {
|
|
142
|
+
"label": "Health Probe Agent",
|
|
143
|
+
"help": "Agent id used for the embedded ACP runtime health probe. Defaults to the first `acp.allowedAgents` entry when that allowlist is set, otherwise to the runtime built-in probe agent (codex). Set this explicitly (for example `opencode` or `claude`) when the default probe agent is not installed or not authenticated, so the whole embedded ACP backend does not get marked unavailable.",
|
|
144
|
+
"advanced": true
|
|
145
|
+
},
|
|
146
|
+
"mcpServers": {
|
|
147
|
+
"label": "MCP Servers",
|
|
148
|
+
"help": "Named MCP server definitions to inject into embedded ACP session bootstrap. Each entry needs a command and can include args and env.",
|
|
149
|
+
"advanced": true
|
|
150
|
+
},
|
|
151
|
+
"agents": {
|
|
152
|
+
"label": "Agent Commands",
|
|
153
|
+
"help": "Optional per-agent command overrides for the embedded ACP runtime.",
|
|
154
|
+
"advanced": true
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
"configContracts": {
|
|
158
|
+
"dangerousFlags": [
|
|
159
|
+
{
|
|
160
|
+
"path": "permissionMode",
|
|
161
|
+
"equals": "approve-all"
|
|
162
|
+
}
|
|
163
|
+
],
|
|
164
|
+
"secretInputs": {
|
|
165
|
+
"bundledDefaultEnabled": false,
|
|
166
|
+
"paths": [
|
|
167
|
+
{
|
|
168
|
+
"path": "mcpServers.*.env.*",
|
|
169
|
+
"expected": "string"
|
|
170
|
+
}
|
|
171
|
+
]
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
package/index.test.ts
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// ACPX tests cover index plugin behavior.
|
|
2
|
+
import type { ACTAgentPluginApi } from "actagent/plugin-sdk/plugin-entry";
|
|
3
|
+
import { createTestPluginApi } from "actagent/plugin-sdk/plugin-test-api";
|
|
4
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
import setupPlugin from "./setup-api.js";
|
|
6
|
+
|
|
7
|
+
const { createAcpxRuntimeServiceMock, tryDispatchAcpReplyHookMock } = vi.hoisted(() => ({
|
|
8
|
+
createAcpxRuntimeServiceMock: vi.fn(),
|
|
9
|
+
tryDispatchAcpReplyHookMock: vi.fn(),
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
vi.mock("./register.runtime.js", () => ({
|
|
13
|
+
createAcpxRuntimeService: createAcpxRuntimeServiceMock,
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
vi.mock("actagent/plugin-sdk/acp-runtime-backend", () => ({
|
|
17
|
+
tryDispatchAcpReplyHook: tryDispatchAcpReplyHookMock,
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
import plugin from "./index.js";
|
|
21
|
+
|
|
22
|
+
type AcpxAutoEnableProbe = Parameters<ACTAgentPluginApi["registerAutoEnableProbe"]>[0];
|
|
23
|
+
|
|
24
|
+
function registerAcpxAutoEnableProbe(): AcpxAutoEnableProbe {
|
|
25
|
+
const probes: AcpxAutoEnableProbe[] = [];
|
|
26
|
+
setupPlugin.register(
|
|
27
|
+
createTestPluginApi({
|
|
28
|
+
registerAutoEnableProbe(probe) {
|
|
29
|
+
probes.push(probe);
|
|
30
|
+
},
|
|
31
|
+
}),
|
|
32
|
+
);
|
|
33
|
+
const probe = probes[0];
|
|
34
|
+
if (!probe) {
|
|
35
|
+
throw new Error("expected ACPX setup plugin to register an auto-enable probe");
|
|
36
|
+
}
|
|
37
|
+
return probe;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
describe("acpx plugin", () => {
|
|
41
|
+
beforeEach(() => {
|
|
42
|
+
vi.clearAllMocks();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("registers the runtime service and reply_dispatch hook", () => {
|
|
46
|
+
const service = { id: "acpx-service", start: vi.fn() };
|
|
47
|
+
createAcpxRuntimeServiceMock.mockReturnValue(service);
|
|
48
|
+
|
|
49
|
+
const api = {
|
|
50
|
+
pluginConfig: { stateDir: "/tmp/acpx" },
|
|
51
|
+
registerService: vi.fn(),
|
|
52
|
+
on: vi.fn(),
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
plugin.register(api as never);
|
|
56
|
+
|
|
57
|
+
expect(createAcpxRuntimeServiceMock).toHaveBeenCalledWith({
|
|
58
|
+
pluginConfig: api.pluginConfig,
|
|
59
|
+
});
|
|
60
|
+
expect(api.registerService).toHaveBeenCalledWith(service);
|
|
61
|
+
expect(api.on).toHaveBeenCalledWith("reply_dispatch", tryDispatchAcpReplyHookMock);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("preserves the ACP reply_dispatch runtime path through the registered hook", async () => {
|
|
65
|
+
const service = { id: "acpx-service", start: vi.fn() };
|
|
66
|
+
createAcpxRuntimeServiceMock.mockReturnValue(service);
|
|
67
|
+
tryDispatchAcpReplyHookMock.mockResolvedValue({
|
|
68
|
+
handled: true,
|
|
69
|
+
queuedFinal: true,
|
|
70
|
+
counts: { tool: 1, block: 0, final: 1 },
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const on = vi.fn();
|
|
74
|
+
const api = createTestPluginApi({
|
|
75
|
+
pluginConfig: { stateDir: "/tmp/acpx" },
|
|
76
|
+
registerService: vi.fn(),
|
|
77
|
+
on,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
plugin.register(api);
|
|
81
|
+
|
|
82
|
+
const hook = on.mock.calls.find(([hookName]) => hookName === "reply_dispatch")?.[1];
|
|
83
|
+
if (!hook) {
|
|
84
|
+
throw new Error("expected reply_dispatch hook to be registered");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const event = {
|
|
88
|
+
ctx: { raw: "reply ctx" },
|
|
89
|
+
runId: "run-1",
|
|
90
|
+
sessionKey: "agent:test:session",
|
|
91
|
+
inboundAudio: false,
|
|
92
|
+
shouldRouteToOriginating: false,
|
|
93
|
+
shouldSendToolSummaries: true,
|
|
94
|
+
sendPolicy: "allow",
|
|
95
|
+
};
|
|
96
|
+
const ctx = {
|
|
97
|
+
cfg: {},
|
|
98
|
+
dispatcher: { dispatch: vi.fn(), getQueuedCounts: vi.fn(), getFailedCounts: vi.fn() },
|
|
99
|
+
recordProcessed: vi.fn(),
|
|
100
|
+
markIdle: vi.fn(),
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
await expect(hook(event, ctx)).resolves.toEqual({
|
|
104
|
+
handled: true,
|
|
105
|
+
queuedFinal: true,
|
|
106
|
+
counts: { tool: 1, block: 0, final: 1 },
|
|
107
|
+
});
|
|
108
|
+
expect(tryDispatchAcpReplyHookMock).toHaveBeenCalledWith(event, ctx);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("declares setup auto-enable reasons for ACPX-owned ACP config", () => {
|
|
112
|
+
const probe = registerAcpxAutoEnableProbe();
|
|
113
|
+
|
|
114
|
+
expect(probe({ config: { acp: { enabled: true } }, env: {} })).toBe("ACP runtime configured");
|
|
115
|
+
expect(probe({ config: { acp: { backend: "acpx" } }, env: {} })).toBe("ACP runtime configured");
|
|
116
|
+
expect(probe({ config: { acp: { enabled: true, backend: "custom-runtime" } }, env: {} })).toBe(
|
|
117
|
+
null,
|
|
118
|
+
);
|
|
119
|
+
});
|
|
120
|
+
});
|
package/index.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ACPX runtime plugin entry. It registers the embedded ACP backend service and
|
|
3
|
+
* wires reply-dispatch hooks into the plugin SDK runtime.
|
|
4
|
+
*/
|
|
5
|
+
import { tryDispatchAcpReplyHook } from "actagent/plugin-sdk/acp-runtime-backend";
|
|
6
|
+
import { createAcpxRuntimeService } from "./register.runtime.js";
|
|
7
|
+
import type { ACTAgentPluginApi } from "./runtime-api.js";
|
|
8
|
+
|
|
9
|
+
const plugin = {
|
|
10
|
+
id: "acpx",
|
|
11
|
+
name: "ACPX Runtime",
|
|
12
|
+
description: "Embedded ACP runtime backend with plugin-owned session and transport management.",
|
|
13
|
+
register(api: ACTAgentPluginApi) {
|
|
14
|
+
api.registerService(
|
|
15
|
+
createAcpxRuntimeService({
|
|
16
|
+
pluginConfig: api.pluginConfig,
|
|
17
|
+
}),
|
|
18
|
+
);
|
|
19
|
+
api.on("reply_dispatch", tryDispatchAcpReplyHook);
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export default plugin;
|