mirror of
https://github.com/volcengine/OpenViking.git
synced 2026-10-01 17:57:49 +08:00
Issue #3522 — the OpenClaw plugin's `config.apiKey` only accepted a plain string, resolved through local `${ENV_VAR}` interpolation. `INSTALL*.md` documented this as a known limitation: users who store their other OpenClaw provider credentials (LLM, TTS, MCP servers) through the standard `{source, id[, provider]}` SecretRef mechanism (env / file mount / exec-backed vault such as 1Password, Vault, gopass) had to keep the OpenViking key as cleartext inside `openclaw.json`. ### config.ts — `string | OpenVikingSecretRef` widening * Add `OpenVikingSecretRef = "env"|"file"|"exec"` discriminated union type, matching the shape OpenClaw core uses for its own credential fields (`env` + `file` implemented in-plugin, `exec` forwarded to `child_process` so providers like `@transmitt0r/openclaw-plugin-onepassword` can manage the OpenViking key without SDK coupling). * Add `resolveSecret()` resolver with explicit, actionable errors: - env: unset var = throw, no silent empty fallback - file: `~` expanded, UTF-8 read, whitespace trimmed; unreadable file rethrows with the OpenViking field name prefixed so config misconfigs surface with a clear label and path - exec: lazy `require("node:child_process").execFileSync(provider,[id])`, stdout trimmed, 15s timeout; errors prefixed with provider + id - unknown source / missing id / missing exec provider = explicit throw * `memoryOpenVikingConfigSchema.parse()` widens `rawApiKey` to `string | OpenVikingSecretRef`, then passes it through `resolveSecret(rawApiKey, "config.apiKey")` *before* the existing `resolveEnvVars` pass. Plain strings transparently fall through `resolveSecret` unchanged, so `${ENV_VAR}` interpolation is preserved 100% backward-compatibly. * `OPENVIKING_API_KEY` env fallback is unchanged and triggers only when the `apiKey` config key is absent — a user who deliberately sets `apiKey: ""` still gets "" (explicitly unauthenticated), not the env fallback. * `uiHints.apiKey.help` documents the SecretRef shape and recommends it. ### openclaw.plugin.json — widening schema + UI hints * `configSchema.properties.apiKey` becomes `oneOf: [string, env ref, file ref, exec ref]`. Each object variant has a `title`, `additionalProperties: false`, `required`, and explicit description per field, so OpenClaw's config UI can render them individually instead of showing a generic JSON object blob. * `uiHints.apiKey.help` matches the new config.ts wording. ### INSTALL.md / INSTALL-ZH.md — SecretRef usage tables Replace the old "plaintext / chmod 0600" caveat bullet with a 3-row table (env / file / exec) showing example JSON + notes (Kubernetes secretKeyRef mount for `file`, 1Password `op://` URL convention for `exec`). The backward-compat string path is retained at the end of the new bullet so existing deployments that haven't migrated yet still get the old permission advice — no surprise behaviour for upgrading users. ### tests/ut/config.test.ts — SecretRef regression suite (10 new cases) Under a new `describe("… SecretRef (#3522)")`: 1. Backward compat: `${OV_KEY}` interpolation still resolves. 2. env source — happy path with a fresh env var. 3. env source — unset var throws, no silent fallback. 4. file source — real `mkdtemp`-created file, trimmed whitespace. Cleanup in `afterEach`. 5. file source — missing-path error message contains readable label + path. 6. exec source — `vi.spyOn(child_process.execFileSync)` asserts provider + args, stdout trimmed. 7. exec source — missing `provider` field errors. 8. Schema validation — unknown `source` and missing `id` each throw with error messages that name the problem. 9. Env fallback boundary — explicit `apiKey: ""` is NOT overridden by OPENVIKING_API_KEY, but `apiKey` absent IS (backward-compat behaviour contract pinned with a test so future refactors can't regress). Covers every branch inside `resolveSecret()`, plus the backward-compat contracts issue #3522 called out.
555 lines
17 KiB
JSON
555 lines
17 KiB
JSON
{
|
|
"id": "openviking",
|
|
"name": "OpenViking",
|
|
"kind": "context-engine",
|
|
"description": "OpenClaw context-engine plugin for memory management — powered by OpenViking",
|
|
"icon": "https://raw.githubusercontent.com/volcengine/OpenViking/main/docs/images/ov-logo-icon.png",
|
|
"activation": {
|
|
"onStartup": true,
|
|
"onCapabilities": [
|
|
"hook",
|
|
"tool"
|
|
]
|
|
},
|
|
"contracts": {
|
|
"tools": [
|
|
"add_resource",
|
|
"add_skill",
|
|
"ov_search",
|
|
"ov_read",
|
|
"ov_multi_read",
|
|
"ov_list",
|
|
"memory_recall",
|
|
"ov_recall_trace",
|
|
"memory_store",
|
|
"memory_forget",
|
|
"ov_archive_search",
|
|
"ov_archive_expand",
|
|
"openviking_tool_result_read",
|
|
"openviking_tool_result_search",
|
|
"openviking_tool_result_list"
|
|
]
|
|
},
|
|
"commandAliases": [
|
|
{
|
|
"name": "setup",
|
|
"kind": "runtime-slash",
|
|
"cliCommand": "openviking"
|
|
},
|
|
{
|
|
"name": "status",
|
|
"kind": "runtime-slash",
|
|
"cliCommand": "openviking"
|
|
}
|
|
],
|
|
"providerAuthEnvVars": {
|
|
"openviking": [
|
|
"OPENVIKING_API_KEY",
|
|
"OPENVIKING_BASE_URL"
|
|
]
|
|
},
|
|
"skills": [
|
|
"./skills/install-openviking-memory",
|
|
"./skills/openviking-context-database"
|
|
],
|
|
"setup": {
|
|
"providers": [
|
|
{
|
|
"id": "openviking",
|
|
"authMethods": [
|
|
"api_key",
|
|
"trusted"
|
|
],
|
|
"envVars": [
|
|
"OPENVIKING_API_KEY",
|
|
"OPENVIKING_BASE_URL"
|
|
],
|
|
"cliFlags": {
|
|
"api_key": [
|
|
"--base-url",
|
|
"--api-key"
|
|
],
|
|
"trusted": [
|
|
"--base-url"
|
|
]
|
|
}
|
|
}
|
|
],
|
|
"requiresRuntime": true,
|
|
"cliCommand": "openclaw openviking setup"
|
|
},
|
|
"uiHints": {
|
|
"baseUrl": {
|
|
"label": "OpenViking Base URL",
|
|
"placeholder": "http://127.0.0.1:1933",
|
|
"help": "HTTP URL when mode is remote (or ${OPENVIKING_BASE_URL})"
|
|
},
|
|
"peer_role": {
|
|
"label": "Peer Role",
|
|
"placeholder": "assistant",
|
|
"help": "Controls which messages include peer_id: none, assistant, or person. New installs default to assistant."
|
|
},
|
|
"peer_prefix": {
|
|
"label": "Peer Prefix",
|
|
"placeholder": "optional-prefix",
|
|
"help": "Optional prefix for assistant peer_id values derived from OpenClaw runtime agent IDs."
|
|
},
|
|
"apiKey": {
|
|
"label": "OpenViking API Key",
|
|
"sensitive": true,
|
|
"placeholder": "${OPENVIKING_API_KEY}",
|
|
"help": "Optional API key for OpenViking server. Accepts a plain string, ${ENV_VAR} interpolation, or a SecretRef object ({source: env/file/exec, id}). Prefer a SecretRef so the key is never stored as plaintext in openclaw.json."
|
|
},
|
|
"headers": {
|
|
"label": "Headers",
|
|
"advanced": true,
|
|
"help": "Optional HTTP headers merged into every OpenViking request."
|
|
},
|
|
"accountId": {
|
|
"label": "Account ID",
|
|
"placeholder": "(derived from API key)",
|
|
"help": "Advanced option. Tenant account ID. Only needed when explicitly sending identity headers, such as root-key or trusted deployments. With a user key the server derives identity from the key.",
|
|
"advanced": true
|
|
},
|
|
"userId": {
|
|
"label": "User ID",
|
|
"placeholder": "(derived from API key)",
|
|
"help": "Advanced option. Tenant user ID. Only needed when explicitly sending identity headers.",
|
|
"advanced": true
|
|
},
|
|
"targetUri": {
|
|
"label": "Search Target URI",
|
|
"placeholder": "viking://user/memories",
|
|
"help": "Default OpenViking target URI for memory search"
|
|
},
|
|
"timeoutMs": {
|
|
"label": "Request Timeout (ms)",
|
|
"placeholder": "15000",
|
|
"advanced": true
|
|
},
|
|
"autoCapture": {
|
|
"label": "Auto-Capture",
|
|
"help": "Extract memories from recent conversation messages via OpenViking sessions"
|
|
},
|
|
"captureMode": {
|
|
"label": "Capture Mode",
|
|
"placeholder": "semantic",
|
|
"advanced": true,
|
|
"help": "semantic captures all eligible user text; keyword uses trigger regex first"
|
|
},
|
|
"captureMaxLength": {
|
|
"label": "Capture Max Length",
|
|
"placeholder": "24000",
|
|
"advanced": true,
|
|
"help": "Maximum sanitized user text length allowed for auto-capture"
|
|
},
|
|
"autoRecall": {
|
|
"label": "Auto-Recall",
|
|
"help": "Inject relevant OpenViking memories into agent context"
|
|
},
|
|
"autoRecallTimeoutMs": {
|
|
"label": "Auto-Recall Timeout (ms)",
|
|
"placeholder": "5000",
|
|
"advanced": true,
|
|
"help": "Outer time budget for the whole auto-recall flow, including search, ranking, and memory reads."
|
|
},
|
|
"recallResources": {
|
|
"label": "Recall Resources",
|
|
"help": "Include resources (viking://resources) in auto-recall and default memory_recall search. Enables account-level shared knowledge retrieval.",
|
|
"advanced": true
|
|
},
|
|
"recallTargetTypes": {
|
|
"label": "Recall Target Types",
|
|
"placeholder": "user,agent",
|
|
"help": "Comma-separated auto-recall and default memory_recall targets: user, agent, resource. Session history is available through ov_archive_search and ov_archive_expand.",
|
|
"advanced": true
|
|
},
|
|
"recallLimit": {
|
|
"label": "Recall Limit",
|
|
"placeholder": "6",
|
|
"advanced": true
|
|
},
|
|
"recallScoreThreshold": {
|
|
"label": "Recall Score Threshold",
|
|
"placeholder": "0.15",
|
|
"advanced": true
|
|
},
|
|
"recallMaxInjectedChars": {
|
|
"label": "Recall Max Injected Chars",
|
|
"placeholder": "4000",
|
|
"advanced": true,
|
|
"help": "Maximum total characters for auto-recall memory injection. Complete memories that do not fit are skipped, not truncated."
|
|
},
|
|
"recallMaxContentChars": {
|
|
"label": "Deprecated Recall Max Content Chars",
|
|
"placeholder": "5000",
|
|
"advanced": true,
|
|
"help": "Deprecated compatibility option and will be removed in a future release. Auto-recall now keeps individual memories intact and uses recallMaxInjectedChars."
|
|
},
|
|
"recallPreferAbstract": {
|
|
"label": "Recall Prefer Abstract",
|
|
"advanced": true,
|
|
"help": "Use memory abstract instead of fetching full content when available"
|
|
},
|
|
"recallTokenBudget": {
|
|
"label": "Deprecated Recall Token Budget",
|
|
"placeholder": "4000",
|
|
"advanced": true,
|
|
"help": "Deprecated compatibility alias and will be removed in a future release. Use recallMaxInjectedChars."
|
|
},
|
|
"bypassSessionPatterns": {
|
|
"label": "Bypass Session Patterns",
|
|
"placeholder": "agent:*:cron:**",
|
|
"help": "Completely bypass OpenViking for matching session keys. Use * within one segment and ** across segments.",
|
|
"advanced": true
|
|
},
|
|
"commitTokenThresholdRatio": {
|
|
"label": "Commit Token Threshold Ratio",
|
|
"placeholder": "0.5",
|
|
"advanced": true,
|
|
"help": "Auto-commit triggers once estimated pending tokens reach this fraction (0-1) of the model context window (e.g. 0.5 = 50%). Set to 0 to commit every turn."
|
|
},
|
|
"commitKeepRecentCount": {
|
|
"label": "Commit Keep Recent Count",
|
|
"placeholder": "10",
|
|
"advanced": true,
|
|
"help": "WM v2: number of most-recent messages kept live after an afterTurn commit. Compact path always uses 0."
|
|
},
|
|
"emitStandardDiagnostics": {
|
|
"label": "Standard diagnostics (diag JSON lines)",
|
|
"advanced": true,
|
|
"help": "Emit structured openviking: diag {...} for assemble/afterTurn. Set false to disable."
|
|
},
|
|
"logFindRequests": {
|
|
"label": "Log find requests",
|
|
"help": "Log tenant routing: /search/find + session messages/commit (X-OpenViking-*; not apiKey). Or set env OPENVIKING_LOG_ROUTING=1 or OPENVIKING_DEBUG=1.",
|
|
"advanced": true
|
|
},
|
|
"traceRecall": {
|
|
"label": "Trace Recall",
|
|
"placeholder": "false",
|
|
"help": "Enable best-effort recall trace recording for debugging recall and search decisions.",
|
|
"advanced": true
|
|
},
|
|
"traceRecallPersist": {
|
|
"label": "Persist Recall Trace",
|
|
"placeholder": "false",
|
|
"help": "Persist recall traces to local JSONL files. Disabled by default.",
|
|
"advanced": true
|
|
},
|
|
"traceRecallDir": {
|
|
"label": "Recall Trace Directory",
|
|
"placeholder": "~/.openclaw/openviking/recall-traces",
|
|
"help": "Directory for persisted recall trace JSONL files.",
|
|
"advanced": true
|
|
},
|
|
"enableAddResourceTool": {
|
|
"label": "Enable Add Resource Tool",
|
|
"placeholder": "false",
|
|
"help": "Disabled by default so search and read flows cannot call add_resource. Set true only when agents should import resources; manual /add-resource remains available.",
|
|
"advanced": true
|
|
},
|
|
"enabledTools": {
|
|
"label": "Enabled Tools",
|
|
"placeholder": "default",
|
|
"help": "Agent-visible tool allowlist. Accepts tool names or groups: default, all, memory, resource_query, import, recall_trace, archive, tool_result. add_resource also requires enableAddResourceTool=true.",
|
|
"advanced": true
|
|
},
|
|
"disabledTools": {
|
|
"label": "Disabled Tools",
|
|
"placeholder": "memory",
|
|
"help": "Agent-visible tool blocklist applied after enabledTools. Accepts the same tool names or groups.",
|
|
"advanced": true
|
|
}
|
|
},
|
|
"configSchema": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"mode": {
|
|
"type": "string",
|
|
"description": "Legacy field kept for backward compatibility. Only 'remote' is supported."
|
|
},
|
|
"baseUrl": {
|
|
"type": "string"
|
|
},
|
|
"peer_role": {
|
|
"type": "string",
|
|
"enum": [
|
|
"none",
|
|
"assistant",
|
|
"person"
|
|
],
|
|
"default": "assistant",
|
|
"description": "Controls which session messages include peer_id."
|
|
},
|
|
"peer_prefix": {
|
|
"type": "string",
|
|
"description": "Optional prefix for assistant peer_id values."
|
|
},
|
|
"serverAuthMode": {
|
|
"type": "string",
|
|
"description": "Deprecated and ignored. Tenant identity headers are controlled by explicit accountId/userId."
|
|
},
|
|
"apiKey": {
|
|
"oneOf": [
|
|
{
|
|
"type": "string",
|
|
"description": "Plain API key or ${ENV_VAR} interpolation. For secret-manager-managed keys, prefer the SecretRef object shape below."
|
|
},
|
|
{
|
|
"type": "object",
|
|
"title": "SecretRef (env)",
|
|
"description": "Read the API key from an environment variable.",
|
|
"additionalProperties": false,
|
|
"required": ["source", "id"],
|
|
"properties": {
|
|
"source": { "type": "string", "const": "env", "description": "Look up the key in process.env[id] at plugin load time. Equivalent to ${ENV} interpolation but declarative and visible in the UI." },
|
|
"id": { "type": "string", "description": "Name of the environment variable. Example: OPENVIKING_API_KEY." }
|
|
}
|
|
},
|
|
{
|
|
"type": "object",
|
|
"title": "SecretRef (file)",
|
|
"description": "Read the API key from a file on disk, e.g. a Kubernetes secretKeyRef mount volume or a 0600-permission managed file. ~ is expanded and trailing whitespace is trimmed.",
|
|
"additionalProperties": false,
|
|
"required": ["source", "id"],
|
|
"properties": {
|
|
"source": { "type": "string", "const": "file" },
|
|
"id": { "type": "string", "description": "Absolute file path or ~/relative path. Example: /etc/secrets/openviking.key." }
|
|
}
|
|
},
|
|
{
|
|
"type": "object",
|
|
"title": "SecretRef (exec)",
|
|
"description": "Read the API key by shelling out to a secret-manager CLI. Matches the shape used by OpenClaw's core provider configs so 1Password, HashiCorp Vault, gopass, etc. can manage the OpenViking key alongside LLM/TTS/MCP credentials.",
|
|
"additionalProperties": false,
|
|
"required": ["source", "provider", "id"],
|
|
"properties": {
|
|
"source": { "type": "string", "const": "exec" },
|
|
"provider": { "type": "string", "description": "CLI binary name on PATH. Examples: op, vault, gopass." },
|
|
"id": { "type": "string", "description": "Argument passed to the provider CLI to select the specific secret, e.g. 'vault kv get -field=key secret/openviking' in plain-arg form; this implementation passes id as a single arg." }
|
|
}
|
|
}
|
|
]
|
|
},
|
|
"headers": {
|
|
"type": "object",
|
|
"additionalProperties": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"accountId": {
|
|
"type": "string"
|
|
},
|
|
"userId": {
|
|
"type": "string"
|
|
},
|
|
"targetUri": {
|
|
"type": "string"
|
|
},
|
|
"timeoutMs": {
|
|
"type": "number"
|
|
},
|
|
"autoCapture": {
|
|
"type": "boolean"
|
|
},
|
|
"captureMode": {
|
|
"type": "string"
|
|
},
|
|
"captureMaxLength": {
|
|
"type": "number"
|
|
},
|
|
"autoRecall": {
|
|
"type": "boolean"
|
|
},
|
|
"autoRecallTimeoutMs": {
|
|
"type": "number",
|
|
"minimum": 1000,
|
|
"maximum": 300000
|
|
},
|
|
"recallResources": {
|
|
"type": "boolean"
|
|
},
|
|
"recallTargetTypes": {
|
|
"oneOf": [
|
|
{
|
|
"type": "string"
|
|
},
|
|
{
|
|
"type": "array",
|
|
"items": {
|
|
"type": "string",
|
|
"enum": [
|
|
"resource",
|
|
"user",
|
|
"agent"
|
|
]
|
|
}
|
|
}
|
|
]
|
|
},
|
|
"recallLimit": {
|
|
"type": "number"
|
|
},
|
|
"recallScoreThreshold": {
|
|
"type": "number"
|
|
},
|
|
"recallMaxInjectedChars": {
|
|
"type": "number"
|
|
},
|
|
"recallMaxContentChars": {
|
|
"type": "number"
|
|
},
|
|
"recallPreferAbstract": {
|
|
"type": "boolean"
|
|
},
|
|
"recallTokenBudget": {
|
|
"type": "number"
|
|
},
|
|
"commitTokenThreshold": {
|
|
"type": "number"
|
|
},
|
|
"commitTokenThresholdRatio": {
|
|
"type": "number",
|
|
"minimum": 0,
|
|
"maximum": 1
|
|
},
|
|
"commitKeepRecentCount": {
|
|
"type": "number",
|
|
"minimum": 0,
|
|
"maximum": 1000
|
|
},
|
|
"bypassSessionPatterns": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"ingestReplyAssist": {
|
|
"type": "boolean"
|
|
},
|
|
"ingestReplyAssistMinSpeakerTurns": {
|
|
"type": "number"
|
|
},
|
|
"ingestReplyAssistMinChars": {
|
|
"type": "number"
|
|
},
|
|
"ingestReplyAssistIgnoreSessionPatterns": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"emitStandardDiagnostics": {
|
|
"type": "boolean"
|
|
},
|
|
"logFindRequests": {
|
|
"type": "boolean"
|
|
},
|
|
"traceRecall": {
|
|
"type": "boolean"
|
|
},
|
|
"traceRecallPersist": {
|
|
"type": "boolean"
|
|
},
|
|
"traceRecallDir": {
|
|
"type": "string"
|
|
},
|
|
"traceRecallRetentionDays": {
|
|
"type": "number",
|
|
"minimum": 1,
|
|
"maximum": 3650
|
|
},
|
|
"traceRecallLoadRecentDays": {
|
|
"type": "number",
|
|
"minimum": 0,
|
|
"maximum": 3650
|
|
},
|
|
"traceRecallMaxEntries": {
|
|
"type": "number",
|
|
"minimum": 1,
|
|
"maximum": 1000000
|
|
},
|
|
"traceRecallMaxResultsPerSearch": {
|
|
"type": "number",
|
|
"minimum": 1,
|
|
"maximum": 1000
|
|
},
|
|
"traceRecallPreviewChars": {
|
|
"type": "number",
|
|
"minimum": 20,
|
|
"maximum": 10000
|
|
},
|
|
"traceRecallQueryMaxChars": {
|
|
"type": "number",
|
|
"minimum": 200,
|
|
"maximum": 200000
|
|
},
|
|
"traceRecallQueryMaxDays": {
|
|
"type": "number",
|
|
"minimum": 1,
|
|
"maximum": 3650
|
|
},
|
|
"traceRecallIncludeContentByDefault": {
|
|
"type": "boolean"
|
|
},
|
|
"traceRecallIncludeRawUserPreview": {
|
|
"type": "boolean"
|
|
},
|
|
"enableAddResourceTool": {
|
|
"type": "boolean"
|
|
},
|
|
"enabledTools": {
|
|
"oneOf": [
|
|
{
|
|
"type": "array",
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
{
|
|
"type": "string"
|
|
}
|
|
]
|
|
},
|
|
"disabledTools": {
|
|
"oneOf": [
|
|
{
|
|
"type": "array",
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
{
|
|
"type": "string"
|
|
}
|
|
]
|
|
},
|
|
"agentExperience": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"enabled": {
|
|
"type": "boolean",
|
|
"default": false,
|
|
"description": "Enable agent experience memory recall. Default is false for conservative rollout."
|
|
},
|
|
"recallLimit": {
|
|
"type": "number"
|
|
},
|
|
"scoreThreshold": {
|
|
"type": "number"
|
|
},
|
|
"maxInjectedChars": {
|
|
"type": "number"
|
|
},
|
|
"minQueryChars": {
|
|
"type": "number"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|