@aionis/openclaw-adapter 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.
@@ -0,0 +1,216 @@
1
+ import { randomUUID } from "node:crypto";
2
+ const DEFAULT_TIMEOUT_MS = 20_000;
3
+ export class AionisHttpClientError extends Error {
4
+ status;
5
+ code;
6
+ details;
7
+ constructor(message, status, code, details) {
8
+ super(message);
9
+ this.status = status;
10
+ this.code = code;
11
+ this.details = details;
12
+ this.name = "AionisHttpClientError";
13
+ }
14
+ }
15
+ function joinUrl(baseUrl, path) {
16
+ return `${baseUrl.replace(/\/+$/, "")}/${path.replace(/^\/+/, "")}`;
17
+ }
18
+ function isRecord(value) {
19
+ return !!value && typeof value === "object" && !Array.isArray(value);
20
+ }
21
+ function extractToolDecision(payload) {
22
+ if (!isRecord(payload))
23
+ return undefined;
24
+ const decision = isRecord(payload.decision) ? payload.decision : undefined;
25
+ const selection = isRecord(payload.selection) ? payload.selection : undefined;
26
+ const denied = Array.isArray(selection?.denied)
27
+ ? selection?.denied
28
+ .map((item) => (isRecord(item) && typeof item.name === "string" ? item.name : null))
29
+ .filter((item) => Boolean(item))
30
+ : [];
31
+ return {
32
+ decision_id: typeof decision?.decision_id === "string" ? decision.decision_id : undefined,
33
+ decision_uri: typeof decision?.decision_uri === "string" ? decision.decision_uri : undefined,
34
+ selected_tool: typeof decision?.selected_tool === "string"
35
+ ? decision.selected_tool
36
+ : typeof selection?.selected === "string"
37
+ ? selection.selected
38
+ : undefined,
39
+ denied_tools: denied,
40
+ explain: typeof payload.selection_summary === "string" ? payload.selection_summary : undefined,
41
+ };
42
+ }
43
+ export class AionisHttpLoopControlClient {
44
+ options;
45
+ constructor(options) {
46
+ this.options = options;
47
+ }
48
+ async post(path, body) {
49
+ const controller = new AbortController();
50
+ const timeout = setTimeout(() => controller.abort(), this.options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
51
+ const headers = {
52
+ "content-type": "application/json",
53
+ "x-request-id": randomUUID(),
54
+ ...(this.options.defaultHeaders ?? {}),
55
+ };
56
+ if (this.options.apiKey)
57
+ headers["x-api-key"] = this.options.apiKey;
58
+ if (this.options.authBearer)
59
+ headers.authorization = `Bearer ${this.options.authBearer}`;
60
+ try {
61
+ const res = await fetch(joinUrl(this.options.baseUrl, path), {
62
+ method: "POST",
63
+ headers,
64
+ body: JSON.stringify(body),
65
+ signal: controller.signal,
66
+ });
67
+ const text = await res.text();
68
+ const payload = text ? JSON.parse(text) : {};
69
+ if (!res.ok) {
70
+ const message = isRecord(payload) && typeof payload.message === "string"
71
+ ? payload.message
72
+ : isRecord(payload) && typeof payload.error === "string"
73
+ ? payload.error
74
+ : `request failed with status ${res.status}`;
75
+ const code = isRecord(payload) && typeof payload.error === "string" ? payload.error : undefined;
76
+ throw new AionisHttpClientError(message, res.status, code, payload);
77
+ }
78
+ return payload;
79
+ }
80
+ catch (error) {
81
+ if (error instanceof AionisHttpClientError)
82
+ throw error;
83
+ if (error instanceof Error && error.name === "AbortError") {
84
+ throw new Error(`aionis request timed out for ${path}`);
85
+ }
86
+ throw error;
87
+ }
88
+ finally {
89
+ clearTimeout(timeout);
90
+ }
91
+ }
92
+ withIdentity(scope, body) {
93
+ return {
94
+ tenant_id: this.options.tenantId,
95
+ actor: this.options.actor,
96
+ scope,
97
+ ...body,
98
+ };
99
+ }
100
+ async contextAssemble(args) {
101
+ const payload = await this.post("/v1/memory/context/assemble", this.withIdentity(args.scope, {
102
+ query_text: args.queryText,
103
+ context: args.context,
104
+ tool_candidates: args.toolCandidates,
105
+ include_rules: true,
106
+ return_layered_context: true,
107
+ }));
108
+ return {
109
+ layered_context: isRecord(payload.layered_context) ? { merged_text: typeof payload.layered_context.merged_text === "string" ? payload.layered_context.merged_text : undefined } : undefined,
110
+ tools: extractToolDecision(isRecord(payload.tools) ? payload.tools : payload),
111
+ };
112
+ }
113
+ async rulesEvaluate(args) {
114
+ return this.post("/v1/memory/rules/evaluate", this.withIdentity(args.scope, {
115
+ context: {
116
+ ...args.context,
117
+ tool_candidates: args.candidates,
118
+ },
119
+ }));
120
+ }
121
+ async toolsSelect(args) {
122
+ const payload = await this.post("/v1/memory/tools/select", this.withIdentity(args.scope, {
123
+ run_id: args.runId,
124
+ context: args.context,
125
+ candidates: args.candidates,
126
+ strict: true,
127
+ }));
128
+ return extractToolDecision(payload) ?? null;
129
+ }
130
+ async toolsDecision(args) {
131
+ return this.post("/v1/memory/tools/decision", this.withIdentity(args.scope, {
132
+ decision_id: args.decisionId,
133
+ decision_uri: args.decisionUri,
134
+ run_id: args.runId,
135
+ }));
136
+ }
137
+ async toolsFeedback(args) {
138
+ return this.post("/v1/memory/tools/feedback", this.withIdentity(args.scope, {
139
+ run_id: args.runId,
140
+ decision_id: args.decisionId,
141
+ decision_uri: args.decisionUri,
142
+ context: args.context,
143
+ candidates: args.candidates,
144
+ selected_tool: args.selectedTool,
145
+ outcome: args.outcome,
146
+ note: args.note,
147
+ input_text: args.inputText,
148
+ }));
149
+ }
150
+ async write(args) {
151
+ return this.post("/v1/memory/write", this.withIdentity(args.scope, {
152
+ input_text: args.inputText,
153
+ metadata: args.metadata,
154
+ }));
155
+ }
156
+ async handoffStore(args) {
157
+ return this.post("/v1/handoff/store", this.withIdentity(args.scope, {
158
+ anchor: args.anchor,
159
+ file_path: args.filePath,
160
+ repo_root: args.repoRoot,
161
+ symbol: args.symbol,
162
+ handoff_kind: args.handoffKind,
163
+ title: args.title,
164
+ summary: args.summary,
165
+ handoff_text: args.handoffText,
166
+ risk: args.risk,
167
+ acceptance_checks: args.acceptanceChecks,
168
+ tags: args.tags,
169
+ target_files: args.targetFiles,
170
+ next_action: args.nextAction,
171
+ must_change: args.mustChange,
172
+ must_remove: args.mustRemove,
173
+ must_keep: args.mustKeep,
174
+ }));
175
+ }
176
+ async replayPlaybookCandidate(args) {
177
+ const payload = await this.post("/v1/memory/replay/playbooks/candidate", this.withIdentity(args.scope, {
178
+ playbook_id: args.playbookId,
179
+ version: args.version,
180
+ deterministic_gate: args.deterministicGate,
181
+ }));
182
+ return {
183
+ playbook: isRecord(payload.playbook) ? {
184
+ playbook_id: typeof payload.playbook.playbook_id === "string" ? payload.playbook.playbook_id : undefined,
185
+ version: typeof payload.playbook.version === "number" ? payload.playbook.version : undefined,
186
+ status: typeof payload.playbook.status === "string" ? payload.playbook.status : undefined,
187
+ name: typeof payload.playbook.name === "string" ? payload.playbook.name : undefined,
188
+ uri: typeof payload.playbook.uri === "string" ? payload.playbook.uri : undefined,
189
+ } : undefined,
190
+ candidate: isRecord(payload.candidate) ? {
191
+ eligible_for_deterministic_replay: Boolean(payload.candidate.eligible_for_deterministic_replay),
192
+ recommended_mode: payload.candidate.recommended_mode === "strict" || payload.candidate.recommended_mode === "guided" || payload.candidate.recommended_mode === "simulate"
193
+ ? payload.candidate.recommended_mode
194
+ : undefined,
195
+ next_action: typeof payload.candidate.next_action === "string" ? payload.candidate.next_action : undefined,
196
+ mismatch_reasons: Array.isArray(payload.candidate.mismatch_reasons)
197
+ ? payload.candidate.mismatch_reasons.filter((item) => typeof item === "string")
198
+ : undefined,
199
+ } : undefined,
200
+ cost_signals: isRecord(payload.cost_signals) ? payload.cost_signals : undefined,
201
+ };
202
+ }
203
+ async replayPlaybookDispatch(args) {
204
+ return this.post("/v1/memory/replay/playbooks/dispatch", this.withIdentity(args.scope, {
205
+ playbook_id: args.playbookId,
206
+ version: args.version,
207
+ params: args.params,
208
+ mode: args.mode,
209
+ max_steps: args.maxSteps,
210
+ deterministic_gate: args.deterministicGate,
211
+ }));
212
+ }
213
+ }
214
+ export function createAionisHttpLoopControlClient(options) {
215
+ return new AionisHttpLoopControlClient(options);
216
+ }
@@ -0,0 +1,10 @@
1
+ export * from "./types/openclaw.js";
2
+ export * from "./types/aionis.js";
3
+ export * from "./types/config.js";
4
+ export * from "./adapter/state.js";
5
+ export * from "./adapter/heuristics.js";
6
+ export * from "./adapter/loop-control-adapter.js";
7
+ export * from "./binding/openclaw-hook-binding.js";
8
+ export * from "./client/aionis-http-client.js";
9
+ export { default as openClawAionisAdapterPlugin } from "./plugin.js";
10
+ export { attachToOpenClawHost as createOpenClawAionisAdapter } from "./binding/openclaw-hook-binding.js";
package/dist/index.js ADDED
@@ -0,0 +1,10 @@
1
+ export * from "./types/openclaw.js";
2
+ export * from "./types/aionis.js";
3
+ export * from "./types/config.js";
4
+ export * from "./adapter/state.js";
5
+ export * from "./adapter/heuristics.js";
6
+ export * from "./adapter/loop-control-adapter.js";
7
+ export * from "./binding/openclaw-hook-binding.js";
8
+ export * from "./client/aionis-http-client.js";
9
+ export { default as openClawAionisAdapterPlugin } from "./plugin.js";
10
+ export { attachToOpenClawHost as createOpenClawAionisAdapter } from "./binding/openclaw-hook-binding.js";
@@ -0,0 +1,15 @@
1
+ import type { OpenClawHostApi } from "./types/openclaw.js";
2
+ declare const plugin: {
3
+ id: string;
4
+ name: string;
5
+ description: string;
6
+ version: string;
7
+ register(api: OpenClawHostApi & {
8
+ pluginConfig?: Record<string, unknown>;
9
+ logger: {
10
+ info: (msg: string) => void;
11
+ warn: (msg: string) => void;
12
+ };
13
+ }): void;
14
+ };
15
+ export default plugin;
package/dist/plugin.js ADDED
@@ -0,0 +1,100 @@
1
+ import { AionisLoopControlAdapter } from "./adapter/loop-control-adapter.js";
2
+ import { attachToOpenClawHost } from "./binding/openclaw-hook-binding.js";
3
+ import { createAionisHttpLoopControlClient } from "./client/aionis-http-client.js";
4
+ function asRecord(value) {
5
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
6
+ }
7
+ function asString(value, fallback = "") {
8
+ return typeof value === "string" ? value.trim() : fallback;
9
+ }
10
+ function asBoolean(value, fallback) {
11
+ return typeof value === "boolean" ? value : fallback;
12
+ }
13
+ function asNumber(value, fallback) {
14
+ return typeof value === "number" && Number.isFinite(value) ? value : fallback;
15
+ }
16
+ function createScopeResolver(scopePrefix, scopeMode, fixedScope) {
17
+ return (ctx) => {
18
+ if (scopeMode === "fixed")
19
+ return fixedScope;
20
+ if (scopeMode === "session") {
21
+ const key = ctx.sessionKey ?? ctx.sessionId ?? "default";
22
+ return `${scopePrefix}:${key}`;
23
+ }
24
+ const workspace = ctx.workspaceDir?.trim();
25
+ if (!workspace)
26
+ return `${scopePrefix}:default`;
27
+ const normalized = workspace.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
28
+ return `${scopePrefix}:${normalized || "workspace"}`;
29
+ };
30
+ }
31
+ function createReplayHintResolver(rawCfg) {
32
+ const playbookId = asString(rawCfg.replayPlaybookId);
33
+ if (!playbookId)
34
+ return undefined;
35
+ const mode = asString(rawCfg.replayMode);
36
+ const maxSteps = asNumber(rawCfg.replayMaxSteps, 0) || undefined;
37
+ const version = asNumber(rawCfg.replayVersion, 0) || undefined;
38
+ return () => ({
39
+ playbookId,
40
+ version,
41
+ mode: mode || undefined,
42
+ maxSteps,
43
+ params: { source: "openclaw-adapter.plugin" },
44
+ deterministicGate: {
45
+ enabled: true,
46
+ prefer_deterministic_execution: true,
47
+ },
48
+ });
49
+ }
50
+ function resolveConfig(rawCfg) {
51
+ const baseUrl = asString(rawCfg.baseUrl, process.env.AIONIS_BASE_URL ?? "http://127.0.0.1:3321");
52
+ const tenantId = asString(rawCfg.tenantId, process.env.AIONIS_TENANT_ID ?? "default");
53
+ const actor = asString(rawCfg.actor, process.env.AIONIS_ACTOR ?? "openclaw-aionis-adapter");
54
+ const scopePrefix = asString(rawCfg.scopePrefix, process.env.AIONIS_SCOPE_PREFIX ?? "openclaw");
55
+ const scopeMode = asString(rawCfg.scopeMode, "project") || "project";
56
+ const fixedScope = asString(rawCfg.scope, `${scopePrefix}:default`);
57
+ return {
58
+ baseUrl,
59
+ apiKey: asString(rawCfg.apiKey) || process.env.AIONIS_API_KEY || undefined,
60
+ authBearer: asString(rawCfg.authBearer) || process.env.AIONIS_AUTH_BEARER || undefined,
61
+ tenantId,
62
+ actor,
63
+ scopeResolver: createScopeResolver(scopePrefix, scopeMode, fixedScope),
64
+ replayHintResolver: createReplayHintResolver(rawCfg),
65
+ thresholds: {
66
+ enabled: asBoolean(rawCfg.enabled, true),
67
+ maxSteps: asNumber(rawCfg.maxSteps, 16),
68
+ maxSameToolStreak: asNumber(rawCfg.maxSameToolStreak, 4),
69
+ maxDuplicateObservationStreak: asNumber(rawCfg.maxDuplicateObservationStreak, 3),
70
+ maxNoProgressStreak: asNumber(rawCfg.maxNoProgressStreak, 3),
71
+ maxEstimatedTokenBurn: asNumber(rawCfg.maxEstimatedTokenBurn, 60000),
72
+ maxBroadTestInvocations: asNumber(rawCfg.maxBroadTestInvocations, 1),
73
+ maxBroadScanInvocations: asNumber(rawCfg.maxBroadScanInvocations, 1),
74
+ },
75
+ strictToolBlocking: asBoolean(rawCfg.strictToolBlocking, true),
76
+ replayDispatchEnabled: asBoolean(rawCfg.replayDispatchEnabled, true),
77
+ handoffFallbackEnabled: asBoolean(rawCfg.handoffFallbackEnabled, true),
78
+ };
79
+ }
80
+ const plugin = {
81
+ id: "openclaw-aionis-adapter",
82
+ name: "OpenClaw Aionis Adapter",
83
+ description: "Tool-loop control adapter for Aionis-backed policy, replay, handoff, and evidence capture.",
84
+ version: "0.1.0",
85
+ register(api) {
86
+ const rawCfg = asRecord(api.pluginConfig);
87
+ const resolved = resolveConfig(rawCfg);
88
+ const client = createAionisHttpLoopControlClient({
89
+ baseUrl: resolved.baseUrl,
90
+ tenantId: resolved.tenantId,
91
+ actor: resolved.actor,
92
+ apiKey: resolved.apiKey,
93
+ authBearer: resolved.authBearer,
94
+ });
95
+ const adapter = new AionisLoopControlAdapter(client, resolved);
96
+ attachToOpenClawHost(api, adapter);
97
+ api.logger.info(`openclaw-aionis-adapter: registered base=${resolved.baseUrl} tenant=${resolved.tenantId} replayDispatch=${resolved.replayDispatchEnabled} handoffFallback=${resolved.handoffFallbackEnabled}`);
98
+ },
99
+ };
100
+ export default plugin;
@@ -0,0 +1,129 @@
1
+ export type AionisToolDecision = {
2
+ decision_id?: string;
3
+ decision_uri?: string;
4
+ selected_tool?: string | null;
5
+ selected?: string | null;
6
+ denied_tools?: string[];
7
+ explain?: string | null;
8
+ request_id?: string | null;
9
+ };
10
+ export type AionisReplayCandidate = {
11
+ playbook_id?: string;
12
+ score?: number;
13
+ reason?: string;
14
+ version?: number;
15
+ recommended_mode?: "simulate" | "strict" | "guided";
16
+ };
17
+ export type ReplayPlaybookHint = {
18
+ playbookId: string;
19
+ version?: number;
20
+ params?: Record<string, unknown>;
21
+ mode?: "simulate" | "strict" | "guided";
22
+ maxSteps?: number;
23
+ deterministicGate?: Record<string, unknown>;
24
+ };
25
+ export type AionisHttpClientOptions = {
26
+ baseUrl: string;
27
+ tenantId?: string;
28
+ actor?: string;
29
+ apiKey?: string;
30
+ authBearer?: string;
31
+ timeoutMs?: number;
32
+ defaultHeaders?: Record<string, string>;
33
+ };
34
+ export type AionisLoopControlClient = {
35
+ contextAssemble?: (args: {
36
+ scope: string;
37
+ queryText: string;
38
+ context: Record<string, unknown>;
39
+ toolCandidates?: string[];
40
+ }) => Promise<{
41
+ layered_context?: {
42
+ merged_text?: string;
43
+ };
44
+ tools?: AionisToolDecision;
45
+ } | null | undefined>;
46
+ rulesEvaluate?: (args: {
47
+ scope: string;
48
+ context: Record<string, unknown>;
49
+ candidates: string[];
50
+ }) => Promise<Record<string, unknown> | null | undefined>;
51
+ toolsSelect?: (args: {
52
+ scope: string;
53
+ runId: string;
54
+ context: Record<string, unknown>;
55
+ candidates: string[];
56
+ }) => Promise<AionisToolDecision | null | undefined>;
57
+ toolsDecision?: (args: {
58
+ scope: string;
59
+ decisionId?: string;
60
+ decisionUri?: string;
61
+ runId?: string;
62
+ }) => Promise<Record<string, unknown> | null | undefined>;
63
+ toolsFeedback?: (args: {
64
+ scope: string;
65
+ runId?: string;
66
+ decisionId?: string;
67
+ decisionUri?: string;
68
+ context: Record<string, unknown>;
69
+ candidates: string[];
70
+ selectedTool: string;
71
+ outcome: "positive" | "negative" | "neutral";
72
+ note?: string;
73
+ inputText: string;
74
+ }) => Promise<Record<string, unknown> | null | undefined>;
75
+ write?: (args: {
76
+ scope: string;
77
+ inputText: string;
78
+ metadata?: Record<string, unknown>;
79
+ }) => Promise<Record<string, unknown> | null | undefined>;
80
+ handoffStore?: (args: {
81
+ scope: string;
82
+ anchor: string;
83
+ filePath: string;
84
+ summary: string;
85
+ handoffText: string;
86
+ repoRoot?: string | null;
87
+ symbol?: string | null;
88
+ handoffKind?: "patch_handoff" | "review_handoff" | "task_handoff";
89
+ title?: string | null;
90
+ risk?: string | null;
91
+ acceptanceChecks?: string[];
92
+ tags?: string[];
93
+ targetFiles?: string[];
94
+ nextAction?: string | null;
95
+ mustChange?: string[];
96
+ mustRemove?: string[];
97
+ mustKeep?: string[];
98
+ }) => Promise<Record<string, unknown> | null | undefined>;
99
+ replayPlaybookCandidate?: (args: {
100
+ scope: string;
101
+ playbookId: string;
102
+ version?: number;
103
+ deterministicGate?: Record<string, unknown>;
104
+ }) => Promise<{
105
+ playbook?: {
106
+ playbook_id?: string;
107
+ version?: number;
108
+ status?: string;
109
+ name?: string | null;
110
+ uri?: string;
111
+ };
112
+ candidate?: {
113
+ eligible_for_deterministic_replay?: boolean;
114
+ recommended_mode?: "simulate" | "strict" | "guided";
115
+ next_action?: string;
116
+ mismatch_reasons?: string[];
117
+ };
118
+ cost_signals?: Record<string, unknown>;
119
+ } | null | undefined>;
120
+ replayPlaybookDispatch?: (args: {
121
+ scope: string;
122
+ playbookId: string;
123
+ version?: number;
124
+ params?: Record<string, unknown>;
125
+ mode?: "simulate" | "strict" | "guided";
126
+ maxSteps?: number;
127
+ deterministicGate?: Record<string, unknown>;
128
+ }) => Promise<Record<string, unknown> | null | undefined>;
129
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,36 @@
1
+ import type { ReplayPlaybookHint } from "./aionis.js";
2
+ export type LoopStopReasonCode = "max_steps_exceeded" | "same_tool_streak_exceeded" | "duplicate_observation_exceeded" | "no_progress_exceeded" | "budget_exceeded" | "policy_denied_only_path" | "replay_dispatch_selected" | "handoff_store_selected";
3
+ export type AdapterThresholds = {
4
+ enabled: boolean;
5
+ maxSteps: number;
6
+ maxSameToolStreak: number;
7
+ maxDuplicateObservationStreak: number;
8
+ maxNoProgressStreak: number;
9
+ maxEstimatedTokenBurn: number;
10
+ maxBroadTestInvocations: number;
11
+ maxBroadScanInvocations: number;
12
+ };
13
+ export type AdapterConfig = {
14
+ tenantId: string;
15
+ actor: string;
16
+ scopeResolver: (ctx: {
17
+ agentId?: string;
18
+ sessionKey?: string;
19
+ sessionId?: string;
20
+ workspaceDir?: string;
21
+ }) => string;
22
+ replayHintResolver?: (ctx: {
23
+ agentId?: string;
24
+ sessionKey?: string;
25
+ sessionId?: string;
26
+ runId?: string;
27
+ workspaceDir?: string;
28
+ toolName?: string;
29
+ toolParams?: Record<string, unknown>;
30
+ }) => ReplayPlaybookHint | null | undefined;
31
+ thresholds: AdapterThresholds;
32
+ strictToolBlocking: boolean;
33
+ replayDispatchEnabled: boolean;
34
+ handoffFallbackEnabled: boolean;
35
+ };
36
+ export declare const DEFAULT_THRESHOLDS: AdapterThresholds;
@@ -0,0 +1,10 @@
1
+ export const DEFAULT_THRESHOLDS = {
2
+ enabled: true,
3
+ maxSteps: 16,
4
+ maxSameToolStreak: 4,
5
+ maxDuplicateObservationStreak: 3,
6
+ maxNoProgressStreak: 3,
7
+ maxEstimatedTokenBurn: 60000,
8
+ maxBroadTestInvocations: 1,
9
+ maxBroadScanInvocations: 1,
10
+ };
@@ -0,0 +1,81 @@
1
+ export type OpenClawLogger = {
2
+ info: (msg: string) => void;
3
+ warn: (msg: string) => void;
4
+ error: (msg: string) => void;
5
+ };
6
+ export type OpenClawHookName = "before_agent_start" | "agent_end" | "before_tool_call" | "after_tool_call" | "tool_result_persist" | "before_message_write" | "session_start" | "session_end";
7
+ export type OpenClawHostApi = {
8
+ logger: OpenClawLogger;
9
+ on?: (eventName: OpenClawHookName, handler: (event: any, ctx: any) => Promise<any> | any) => void;
10
+ };
11
+ export type OpenClawAgentRunContext = {
12
+ agentId?: string;
13
+ sessionKey?: string;
14
+ sessionId?: string;
15
+ workspaceDir?: string;
16
+ trigger?: string;
17
+ channelId?: string;
18
+ };
19
+ export type OpenClawToolCallContext = OpenClawAgentRunContext & {
20
+ runId?: string;
21
+ toolName: string;
22
+ toolCallId?: string;
23
+ };
24
+ export type BeforeAgentStartEvent = {
25
+ prompt: string;
26
+ messages?: unknown[];
27
+ };
28
+ export type BeforeAgentStartResult = {
29
+ prependContext?: string;
30
+ systemPrompt?: string;
31
+ modelOverride?: string;
32
+ providerOverride?: string;
33
+ };
34
+ export type AgentEndEvent = {
35
+ messages: unknown[];
36
+ success: boolean;
37
+ error?: string;
38
+ durationMs?: number;
39
+ };
40
+ export type BeforeToolCallEvent = {
41
+ toolName: string;
42
+ params: Record<string, unknown>;
43
+ runId?: string;
44
+ toolCallId?: string;
45
+ };
46
+ export type BeforeToolCallResult = {
47
+ params?: Record<string, unknown>;
48
+ block?: boolean;
49
+ blockReason?: string;
50
+ };
51
+ export type AfterToolCallEvent = {
52
+ toolName: string;
53
+ params: Record<string, unknown>;
54
+ runId?: string;
55
+ toolCallId?: string;
56
+ result?: unknown;
57
+ error?: string;
58
+ durationMs?: number;
59
+ };
60
+ export type ToolResultPersistEvent = {
61
+ toolName?: string;
62
+ toolCallId?: string;
63
+ message: Record<string, unknown>;
64
+ isSynthetic?: boolean;
65
+ };
66
+ export type ToolResultPersistResult = {
67
+ message?: Record<string, unknown>;
68
+ };
69
+ export type BeforeMessageWriteEvent = {
70
+ message: Record<string, unknown>;
71
+ sessionKey?: string;
72
+ agentId?: string;
73
+ };
74
+ export type BeforeMessageWriteResult = {
75
+ block?: boolean;
76
+ message?: Record<string, unknown>;
77
+ };
78
+ export type SessionLifecycleEvent = {
79
+ sessionId: string;
80
+ sessionKey?: string;
81
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,34 @@
1
+ {
2
+ "agents": {
3
+ "defaults": {
4
+ "workspace": "/absolute/path/to/workspace",
5
+ "model": {
6
+ "primary": "google/gemini-3-flash-preview"
7
+ }
8
+ }
9
+ },
10
+ "plugins": {
11
+ "allow": ["openclaw-aionis-adapter"],
12
+ "entries": {
13
+ "openclaw-aionis-adapter": {
14
+ "enabled": true,
15
+ "config": {
16
+ "baseUrl": "http://127.0.0.1:3321",
17
+ "tenantId": "default",
18
+ "actor": "openclaw",
19
+ "scopeMode": "project",
20
+ "strictToolBlocking": true,
21
+ "replayDispatchEnabled": true,
22
+ "handoffFallbackEnabled": true,
23
+ "maxSteps": 8,
24
+ "maxSameToolStreak": 2,
25
+ "maxDuplicateObservationStreak": 2,
26
+ "maxNoProgressStreak": 2,
27
+ "maxEstimatedTokenBurn": 12000,
28
+ "maxBroadTestInvocations": 1,
29
+ "maxBroadScanInvocations": 1
30
+ }
31
+ }
32
+ }
33
+ }
34
+ }