From eea8fd767298b3ed8b9893bb015a562e7e7e91da Mon Sep 17 00:00:00 2001 From: cloudsigma Date: Tue, 28 Jul 2026 18:32:05 +0000 Subject: [PATCH] fix: inject affinity metadata on simple completion calls --- index.ts | 12 ++++++++++-- package-lock.json | 4 ++-- package.json | 2 +- test/simple-completion-affinity.test.ts | 21 +++++++++++++++++++++ test/smoke.mjs | 4 ++-- 5 files changed, 36 insertions(+), 7 deletions(-) create mode 100644 test/simple-completion-affinity.test.ts diff --git a/index.ts b/index.ts index 30944a7..2d137d0 100644 --- a/index.ts +++ b/index.ts @@ -29,7 +29,7 @@ const REQUESTER_RUNTIME_SCHEMA_VERSION = "2026-06-04" const REQUESTER_RUNTIME_SOURCE = "openclaw-taas-affinity" const GIT_PROBE_TIMEOUT_MS = 250 const LAST_ROUTE_LIMIT = 256 -const PLUGIN_VERSION = "0.8.0" +const PLUGIN_VERSION = "0.9.0" // OpenClaw stores active registry state (including workspaceDir) on globalThis // under this well-known symbol key. @@ -498,14 +498,22 @@ export default { "pin sessions to the same upstream slot from turn 1, maximising prompt-cache hit rates.", register(api: OpenClawPluginApi) { + // The runtime supports wrapSimpleCompletionStreamFn, but the installed + // plugin-sdk ProviderPlugin declaration lags that optional hook. Keep the + // compatibility cast scoped to this registration object. api.registerProvider({ id: "taas-affinity-hook", label: "CloudSigma TaaS Token Cache Optimizer", hookAliases: ["cloudsigma", "cloudsigma-staging"], auth: [], + // Full agent/tool runs use wrapStreamFn. Internal/background/simple + // completions use a separate OpenClaw hook; omitting it created a + // second identity-less population on the same Snowcrash API key. + // Both surfaces must inject identical session/sticky metadata. wrapStreamFn: buildWrapper, + wrapSimpleCompletionStreamFn: buildWrapper, resolveTransportTurnState: buildTransportTurnState, - }) + } as any) if (typeof api.registerGatewayMethod === "function") api.registerGatewayMethod( "taas.completion.deliver", diff --git a/package-lock.json b/package-lock.json index b726b3f..fc54d8d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "openclaw-taas-affinity", - "version": "0.8.0", + "version": "0.9.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "openclaw-taas-affinity", - "version": "0.8.0", + "version": "0.9.0", "license": "MIT", "devDependencies": { "@types/node": "^22.0.0", diff --git a/package.json b/package.json index 972394a..7b7a1c2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "openclaw-taas-affinity", - "version": "0.8.0", + "version": "0.9.0", "description": "OpenClaw provider plugin \u2014 CloudSigma TaaS session affinity. Injects a stable X-Session-Id header per conversation so TaaS can pin the session to the same OAuth token / Bedrock region / Claude Code node, maximising prompt-cache hit rates.", "type": "module", "main": "dist/index.js", diff --git a/test/simple-completion-affinity.test.ts b/test/simple-completion-affinity.test.ts new file mode 100644 index 0000000..b9748b8 --- /dev/null +++ b/test/simple-completion-affinity.test.ts @@ -0,0 +1,21 @@ +import assert from "node:assert/strict" +import test from "node:test" +import plugin from "../index.ts" + +test("simple completion hook is registered and shares affinity injection", async () => { + let registered: any + ;(plugin as any).register({ registerProvider: (cfg: any) => { registered = cfg }, registerGatewayMethod: () => {} }) + assert.equal(registered.wrapSimpleCompletionStreamFn, registered.wrapStreamFn) + let innerOptions: any + const inner = async function* (_m: any, _c: any, options: any) { innerOptions = options; yield { type: "start" } } + const wrapped = registered.wrapSimpleCompletionStreamFn({ + provider: "cloudsigma", modelId: "gpt-5.6-sol", agentId: "main", + workspaceDir: "/home/cloudsigma/.openclaw/workspace", streamFn: inner, + }) + const gen = wrapped("gpt-5.6-sol", {}, { onPayload: async (p: any) => p }) + await gen.next() + const patched = await innerOptions.onPayload({ model: "gpt-5.6-sol", messages: [{ role: "user", content: "hi" }] }, "gpt-5.6-sol") + assert.ok(patched.metadata?.session_id?.startsWith("oc:")) + assert.equal(patched.metadata?.sticky_key, patched.metadata?.session_id) + assert.equal(patched.metadata?.openclaw_correlation?.agent_id, "main") +}) diff --git a/test/smoke.mjs b/test/smoke.mjs index 2faeb65..3772534 100644 --- a/test/smoke.mjs +++ b/test/smoke.mjs @@ -68,7 +68,7 @@ const transportState = provider.resolveTransportTurnState({ sessionId: "smoke-lo assert.equal(transportState.headers["X-Session-Id"], "smoke-local-session") assert.equal(transportState.headers["X-OpenClaw-Session-Id"], transportState.headers["X-Session-Id"]) -assert.equal(transportState.headers["X-OpenClaw-Plugin-Version"], "0.8.0") +assert.equal(transportState.headers["X-OpenClaw-Plugin-Version"], "0.9.0") assert.equal(transportState.headers["X-OpenClaw-Turn-Id"], "turn-smoke") assert.equal(transportState.headers["X-OpenClaw-Attempt"], "1") @@ -93,7 +93,7 @@ assert.equal(capturedPayload.metadata.requester_runtime.tool_execution, "directi assert.equal("available_bridges" in capturedPayload.metadata.requester_runtime, false) assert.equal(capturedPayload.metadata.openclaw_correlation.schema_version, "2026-06-05") assert.equal(capturedPayload.metadata.openclaw_correlation.source, "openclaw-taas-affinity") -assert.equal(capturedPayload.metadata.openclaw_correlation.plugin_version, "0.8.0") +assert.equal(capturedPayload.metadata.openclaw_correlation.plugin_version, "0.9.0") assert.equal(capturedPayload.metadata.openclaw_correlation.session_id, localSessionA) assert.equal(capturedPayload.metadata.openclaw_correlation.sticky_key, localSessionA) assert.equal(capturedPayload.metadata.openclaw_correlation.provider, "cloudsigma")