@alignfirst/service-openclaw-plugin 0.4.0 → 0.4.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/README.md
CHANGED
|
@@ -73,7 +73,7 @@ The plugin commits a pending record before dispatching `Take over this thread.`
|
|
|
73
73
|
|
|
74
74
|
The message body is static: it carries no starter copy, routing fields, or handoff ID. The playbook routes by thread metadata, claims the current session, and reads the visible starter and human replies through thread history. The nudge supplies no missing input or approval. A takeover turn with nothing to report ends with `HEARTBEAT_OK`; the deterministic gateway probe confirmed that `NO_REPLY` still triggers isolated finalization on this path.
|
|
75
75
|
|
|
76
|
-
The plugin starts the thread session and does nothing after that.
|
|
76
|
+
The plugin starts the thread session and does nothing after that. `aligndev code` completion uses OpenClaw's own completion path.
|
|
77
77
|
|
|
78
78
|
Each takeover turn gets the regular agent budget from `agents.defaults.timeoutSeconds`, including the 48-hour OpenClaw default and the unlimited `0` value.
|
|
79
79
|
|
|
@@ -111,7 +111,7 @@ npm run lint --workspace @alignfirst/service-openclaw-plugin
|
|
|
111
111
|
```
|
|
112
112
|
|
|
113
113
|
The ordinary test command excludes the real-gateway suite. To exercise the package as an external
|
|
114
|
-
plugin against the pinned
|
|
114
|
+
plugin against the OpenClaw runtime pinned in the root lockfile, including Slack/Discord delivery, concurrent human messages, duplicate starts, same-session continuation, and abrupt restart recovery:
|
|
115
115
|
|
|
116
116
|
```bash
|
|
117
117
|
KEEP_THREAD_HANDOFF_ARTIFACTS=1 npm run test:integration --workspace @alignfirst/service-openclaw-plugin
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { registerThreadHandoffCli } from "./cli.js";
|
|
2
|
-
import {
|
|
2
|
+
import { processShared } from "./process-shared.js";
|
|
3
|
+
import { createReceiptCache, createReceiptCoordinator } from "./receipts.js";
|
|
3
4
|
import { createRunIdCache } from "./run-ids.js";
|
|
4
5
|
import { createHandoffService } from "./service.js";
|
|
5
6
|
import { createHandoffStore, resolveDatabasePath } from "./state.js";
|
|
@@ -16,8 +17,13 @@ export function registerThreadHandoff(api) {
|
|
|
16
17
|
store ??= createHandoffStore(api.runtime.state.resolveStateDir());
|
|
17
18
|
return store;
|
|
18
19
|
};
|
|
19
|
-
const receipts = createReceiptCoordinator({
|
|
20
|
-
|
|
20
|
+
const receipts = createReceiptCoordinator({
|
|
21
|
+
configuration,
|
|
22
|
+
getStore,
|
|
23
|
+
logger: api.logger,
|
|
24
|
+
cache: processShared("receipt-cache/v1", createReceiptCache),
|
|
25
|
+
});
|
|
26
|
+
const runIds = processShared("run-ids/v1", () => createRunIdCache());
|
|
21
27
|
const service = createHandoffService({
|
|
22
28
|
runtime: api.runtime,
|
|
23
29
|
configuration,
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns the value stored under `name` for the whole gateway process, created on first use.
|
|
3
|
+
*
|
|
4
|
+
* Since 2026.9.8, OpenClaw evaluates this plugin more than once in one gateway process: a turn
|
|
5
|
+
* takes its tools from the gateway's registration and runs its tool hooks in its own. State that
|
|
6
|
+
* both a tool and a hook read must outlive a single registration.
|
|
7
|
+
*/
|
|
8
|
+
export declare function processShared<T>(name: string, create: () => T): T;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns the value stored under `name` for the whole gateway process, created on first use.
|
|
3
|
+
*
|
|
4
|
+
* Since 2026.9.8, OpenClaw evaluates this plugin more than once in one gateway process: a turn
|
|
5
|
+
* takes its tools from the gateway's registration and runs its tool hooks in its own. State that
|
|
6
|
+
* both a tool and a hook read must outlive a single registration.
|
|
7
|
+
*/
|
|
8
|
+
export function processShared(name, create) {
|
|
9
|
+
const slots = globalThis;
|
|
10
|
+
const key = Symbol.for(`@alignfirst/service-openclaw-plugin/${name}`);
|
|
11
|
+
if (!(key in slots))
|
|
12
|
+
slots[key] = create();
|
|
13
|
+
// The slot holds what `create` returned under this versioned name.
|
|
14
|
+
return slots[key];
|
|
15
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { OpenClawPluginToolContext, PluginLogger } from "openclaw/plugin-sdk/plugin-entry";
|
|
2
2
|
import type { HandoffStore } from "./state.js";
|
|
3
|
-
import type { DeliveryReceipt, PluginConfiguration, ReceiptIdentity } from "./types.js";
|
|
3
|
+
import type { DeliveryReceipt, PluginConfiguration, ReceiptIdentity, SourceContext } from "./types.js";
|
|
4
4
|
export interface ReceiptCoordinator {
|
|
5
5
|
captureContext(context: OpenClawPluginToolContext): void;
|
|
6
6
|
observe(event: ToolObservation, context: HookContext): void;
|
|
@@ -17,10 +17,21 @@ interface HookContext {
|
|
|
17
17
|
sessionKey?: string;
|
|
18
18
|
sessionId?: string;
|
|
19
19
|
}
|
|
20
|
+
/** Captured contexts and observation errors, shared by every registration of the plugin. */
|
|
21
|
+
export interface ReceiptCache {
|
|
22
|
+
contexts: Map<string, CachedContext>;
|
|
23
|
+
observationErrors: Map<string, Error>;
|
|
24
|
+
}
|
|
25
|
+
export interface CachedContext {
|
|
26
|
+
source: SourceContext;
|
|
27
|
+
capturedAt: number;
|
|
28
|
+
}
|
|
20
29
|
export declare function createReceiptCoordinator(params: {
|
|
21
30
|
configuration: PluginConfiguration;
|
|
22
31
|
getStore: () => HandoffStore;
|
|
23
32
|
logger: PluginLogger;
|
|
33
|
+
cache?: ReceiptCache;
|
|
24
34
|
now?: () => number;
|
|
25
35
|
}): ReceiptCoordinator;
|
|
36
|
+
export declare function createReceiptCache(): ReceiptCache;
|
|
26
37
|
export {};
|
|
@@ -7,8 +7,7 @@ const RECEIPT_WAIT_MS = 1_000;
|
|
|
7
7
|
const RECEIPT_POLL_MS = 25;
|
|
8
8
|
export function createReceiptCoordinator(params) {
|
|
9
9
|
const now = params.now ?? Date.now;
|
|
10
|
-
const contexts =
|
|
11
|
-
const observationErrors = new Map();
|
|
10
|
+
const { contexts, observationErrors } = params.cache ?? createReceiptCache();
|
|
12
11
|
return {
|
|
13
12
|
captureContext(context) {
|
|
14
13
|
const source = readSourceContext(context, params.configuration);
|
|
@@ -61,6 +60,9 @@ export function createReceiptCoordinator(params) {
|
|
|
61
60
|
},
|
|
62
61
|
};
|
|
63
62
|
}
|
|
63
|
+
export function createReceiptCache() {
|
|
64
|
+
return { contexts: new Map(), observationErrors: new Map() };
|
|
65
|
+
}
|
|
64
66
|
function readCachedSource(contexts, context, now) {
|
|
65
67
|
const sessionKey = nonempty(context.sessionKey);
|
|
66
68
|
const sessionId = nonempty(context.sessionId);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alignfirst/service-openclaw-plugin",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "OpenClaw capabilities for the AlignFirst Dev Kit.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"alignfirst",
|
|
@@ -43,11 +43,11 @@
|
|
|
43
43
|
"typebox": "~1.3.23"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"@alignfirst/openclaw-channel-mock-core": "0.10.
|
|
46
|
+
"@alignfirst/openclaw-channel-mock-core": "0.10.3",
|
|
47
47
|
"@types/node": "~26.5.0",
|
|
48
|
-
"openclaw": "~2026.9.
|
|
48
|
+
"openclaw": "~2026.9.8",
|
|
49
49
|
"rimraf": "~6.1.3",
|
|
50
50
|
"typescript": "~7.0.2",
|
|
51
|
-
"vitest": "~
|
|
51
|
+
"vitest": "~5.0.1"
|
|
52
52
|
}
|
|
53
53
|
}
|