clawdi 0.13.28 → 0.13.30
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/dist/index.js +526 -497
- package/egress-addon/clawdi_egress_addon.py +39 -2
- package/package.json +1 -1
- package/skills/clawdi/SKILL.md +47 -21
- package/skills/hosted-versions/1/clawdi/SKILL.md +45 -2
|
@@ -10,6 +10,7 @@ from __future__ import annotations
|
|
|
10
10
|
import json
|
|
11
11
|
import os
|
|
12
12
|
import re
|
|
13
|
+
from datetime import UTC, datetime
|
|
13
14
|
from pathlib import Path
|
|
14
15
|
from typing import Any
|
|
15
16
|
from urllib.parse import parse_qs, urlsplit
|
|
@@ -283,6 +284,8 @@ def profile_matches(flow: Any, profile: dict[str, Any], secrets: dict[str, str])
|
|
|
283
284
|
match = profile.get("match")
|
|
284
285
|
if not isinstance(match, dict):
|
|
285
286
|
return False
|
|
287
|
+
if not match_time_is_valid(match):
|
|
288
|
+
return False
|
|
286
289
|
kind = profile.get("kind")
|
|
287
290
|
request_scheme = request_profile_scheme(flow)
|
|
288
291
|
configured_scheme = match.get("scheme")
|
|
@@ -318,6 +321,21 @@ def profile_matches(flow: Any, profile: dict[str, Any], secrets: dict[str, str])
|
|
|
318
321
|
return True
|
|
319
322
|
|
|
320
323
|
|
|
324
|
+
def match_time_is_valid(match: dict[str, Any], now: datetime | None = None) -> bool:
|
|
325
|
+
if "notAfter" not in match:
|
|
326
|
+
return True
|
|
327
|
+
not_after = match.get("notAfter")
|
|
328
|
+
if not isinstance(not_after, str):
|
|
329
|
+
return False
|
|
330
|
+
try:
|
|
331
|
+
parsed = datetime.fromisoformat(not_after.replace("Z", "+00:00"))
|
|
332
|
+
except ValueError:
|
|
333
|
+
return False
|
|
334
|
+
if parsed.tzinfo is None:
|
|
335
|
+
return False
|
|
336
|
+
return (now or datetime.now(UTC)) < parsed
|
|
337
|
+
|
|
338
|
+
|
|
321
339
|
def host_matches(flow: Any, profile: dict[str, Any]) -> bool:
|
|
322
340
|
match = profile.get("match")
|
|
323
341
|
if not isinstance(match, dict):
|
|
@@ -407,9 +425,14 @@ def matcher_matches(value: str | None, matcher: Any, secrets: dict[str, str]) ->
|
|
|
407
425
|
return False
|
|
408
426
|
if matcher_type == "equals":
|
|
409
427
|
return value == f"{matcher.get('prefix', '')}{matcher.get('value', '')}"
|
|
410
|
-
if matcher_type
|
|
428
|
+
if matcher_type in {"secretRefEquals", "secretRefPrefix"}:
|
|
411
429
|
secret = secrets.get(str(matcher.get("secretRef", "")))
|
|
412
|
-
|
|
430
|
+
if secret is None:
|
|
431
|
+
return False
|
|
432
|
+
expected = f"{matcher.get('prefix', '')}{secret}{matcher.get('suffix', '')}"
|
|
433
|
+
if matcher_type == "secretRefEquals":
|
|
434
|
+
return value == expected
|
|
435
|
+
return value.startswith(expected)
|
|
413
436
|
return False
|
|
414
437
|
|
|
415
438
|
|
|
@@ -456,6 +479,8 @@ def split_authority(authority: str) -> tuple[str, int | None]:
|
|
|
456
479
|
|
|
457
480
|
def apply_rewrite_headers(flow: Any, profile: dict[str, Any], secrets: dict[str, str]) -> None:
|
|
458
481
|
rewrite = profile.get("rewrite") if isinstance(profile.get("rewrite"), dict) else {}
|
|
482
|
+
for name in rewrite.get("removeHeaders", []):
|
|
483
|
+
header_delete(flow.request.headers, str(name))
|
|
459
484
|
for name, setter in rewrite.get("setHeaders", {}).items():
|
|
460
485
|
resolved = resolve_header_setter(setter, secrets)
|
|
461
486
|
if resolved is not None:
|
|
@@ -557,6 +582,18 @@ def header_set(headers: Any, name: str, value: str) -> None:
|
|
|
557
582
|
headers[name] = value
|
|
558
583
|
|
|
559
584
|
|
|
585
|
+
def header_delete(headers: Any, name: str) -> None:
|
|
586
|
+
lower = name.lower()
|
|
587
|
+
for key in list(getattr(headers, "keys", lambda: [])()):
|
|
588
|
+
if str(key).lower() != lower:
|
|
589
|
+
continue
|
|
590
|
+
try:
|
|
591
|
+
del headers[key]
|
|
592
|
+
except (KeyError, TypeError):
|
|
593
|
+
pass
|
|
594
|
+
return
|
|
595
|
+
|
|
596
|
+
|
|
560
597
|
def redact_url(url: str, profile: dict[str, Any] | None) -> str:
|
|
561
598
|
redacted = url
|
|
562
599
|
if not profile:
|
package/package.json
CHANGED
package/skills/clawdi/SKILL.md
CHANGED
|
@@ -77,6 +77,39 @@ MUST call `session_search` when:
|
|
|
77
77
|
|
|
78
78
|
When the user's request is **conceptual** ("how do I usually do X"), prefer `memory_search`. When they want to **revisit a specific past conversation** ("the session where..."), use `session_search`. When unsure, try `memory_search` first (cheaper, faster), fall back to `session_search` if empty.
|
|
79
79
|
|
|
80
|
+
## Projects
|
|
81
|
+
|
|
82
|
+
Three read-only tools expose the caller's visible Project context:
|
|
83
|
+
|
|
84
|
+
- `project_current` — Read the current or runtime-bound Project.
|
|
85
|
+
- `project_list` — List visible Projects.
|
|
86
|
+
- `project_get` — Read one visible Project by UUID.
|
|
87
|
+
|
|
88
|
+
Hosted runtimes see only their bound Project. Treat a not-found response as an
|
|
89
|
+
access boundary as well as a possible unknown UUID; do not try to bypass it
|
|
90
|
+
through another tool.
|
|
91
|
+
|
|
92
|
+
## Vault Metadata
|
|
93
|
+
|
|
94
|
+
Two read-only MCP tools expose safe Vault metadata without secret values:
|
|
95
|
+
|
|
96
|
+
- `vault_list` — List Vault attachments and key counts for visible Projects.
|
|
97
|
+
- `vault_get` — List key names, provenance, and exact `clawdi://` references for one attached Vault.
|
|
98
|
+
|
|
99
|
+
Use `vault_resolve` only when the current task requires one referenced plaintext value. Pass
|
|
100
|
+
the exact Project-scoped reference. Treat the result as sensitive: never echo it, save it to
|
|
101
|
+
Memory, or include it in logs.
|
|
102
|
+
|
|
103
|
+
The metadata tools never resolve or return plaintext. Never imply that a returned key name
|
|
104
|
+
is a secret value. Preserve their exact references for `vault_resolve` or when passing them
|
|
105
|
+
to an authorized runtime:
|
|
106
|
+
|
|
107
|
+
- `clawdi://project/<project-id>/vault/<vault>/field/<field>`
|
|
108
|
+
- `clawdi://project/<project-id>/vault/<vault>/section/<section>/field/<field>`
|
|
109
|
+
|
|
110
|
+
Use the live schemas from the `clawdi` MCP server as authoritative; the local
|
|
111
|
+
stdio command only transports the protocol.
|
|
112
|
+
|
|
80
113
|
## Connectors
|
|
81
114
|
|
|
82
115
|
Use the Composio Tool Router meta-tools returned by `tools/list` on the `clawdi` MCP server.
|
|
@@ -112,30 +145,23 @@ Treat their live names and schemas as authoritative; never assume a fixed meta-t
|
|
|
112
145
|
fields, and termination signals exactly as exposed. Select an account only when the schema
|
|
113
146
|
supports it, and use additional or future meta-tools only according to their live schemas.
|
|
114
147
|
|
|
115
|
-
## Vault
|
|
116
|
-
|
|
117
|
-
When the user asks to migrate secrets into Clawdi Vault or script secret writes, prefer the CLI over raw HTTP calls:
|
|
148
|
+
## Vault Management
|
|
118
149
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
- Use `clawdi vault attach <vault> --project <project>` to make an existing Vault available in another Project.
|
|
126
|
-
- Use `clawdi vault detach <vault> --project <project>` to remove one Project's access without deleting keys.
|
|
127
|
-
- Use `clawdi vault rm <vault>/<section>/<field> --global --yes` only when the key should be deleted from the shared Vault for every attached Project.
|
|
128
|
-
- Prefer exact `clawdi://project/...` references printed by the CLI. Do not print plaintext secret values unless the user explicitly asks for them.
|
|
150
|
+
Vault mutation is intentionally not an Agent MCP capability. Do not use raw HTTP, daemon
|
|
151
|
+
control RPC, or execute foreground Vault CLI commands on the user's behalf. When the user
|
|
152
|
+
asks to write, import, attach, detach, or delete Vault data, explain that a human operator
|
|
153
|
+
must perform it and provide the safest exact foreground command. Prefer `clawdi vault set
|
|
154
|
+
KEY --prompt` for one value and `clawdi vault import ...` for migrations; never place a
|
|
155
|
+
plaintext secret in command arguments or your response.
|
|
129
156
|
|
|
130
|
-
## AI Provider
|
|
157
|
+
## AI Provider Management
|
|
131
158
|
|
|
132
|
-
|
|
159
|
+
Provider configuration is also a human operator workflow, not an Agent MCP capability. Do
|
|
160
|
+
not execute provider CLI commands or handle provider credentials on the user's behalf. When
|
|
161
|
+
asked, provide an exact `clawdi ai-provider` command for the operator to run and explain its
|
|
162
|
+
effect; suggest `validate` or a non-live `test` before any explicitly requested live probe.
|
|
133
163
|
|
|
134
|
-
-
|
|
135
|
-
-
|
|
136
|
-
- Check local auth availability with `clawdi ai-provider test <provider-id>`; add `--live` only when the user explicitly wants a real provider API probe.
|
|
137
|
-
- Apply agent config with `clawdi ai-provider apply --engine codex|hermes|openclaw --dry-run` first, then run without `--dry-run` if the diff is acceptable.
|
|
138
|
-
- Connect Codex OAuth with `clawdi ai-provider connect <provider-id> --tool codex`; use `--callback manual` when loopback localhost cannot be reached.
|
|
139
|
-
- Materialize a stored provider auth profile with `clawdi ai-provider materialize-auth <provider-id>`.
|
|
164
|
+
- Treat the local Provider Catalog as multi-record metadata. Do not activate it into local agent config; Core Hosted activation is supplied by the runtime manifest/controller, whose configured runtime binds exactly one provider and whose unmanaged runtime binds none.
|
|
165
|
+
- Keep Codex OAuth ownership singular across Hosted runtimes. Hermes/OpenClaw native refresh, revoke, and ownership state belongs to Hosted convergence, not a local CLI materialization command.
|
|
140
166
|
- Default export/import is metadata-only; `--include-secrets` requires passphrase-encrypted secret export.
|
|
141
167
|
- BYOK model requests go directly from the agent runtime to the configured provider. Clawdi stores metadata and secret references but is not a model proxy.
|
|
@@ -1,11 +1,27 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: clawdi
|
|
3
|
-
description: "
|
|
3
|
+
description: "Use the user's account-wide long-term memory and past agent sessions; inspect the current Hosted Project and safe Vault metadata; use connected services such as Gmail, GitHub, Notion, Drive, and Calendar; and read Clawdi share URLs."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Clawdi Cloud
|
|
7
7
|
|
|
8
|
-
Use Clawdi Cloud tools through the `clawdi` MCP server.
|
|
8
|
+
Use Clawdi Cloud tools through the `clawdi` MCP server. Treat the live tool schemas as authoritative.
|
|
9
|
+
|
|
10
|
+
## Memory
|
|
11
|
+
|
|
12
|
+
Memory is shared across the user's Hosted agents, not isolated to the current agent.
|
|
13
|
+
|
|
14
|
+
- `memory_search` — Search durable memory by natural-language query.
|
|
15
|
+
- `memory_add` — Save a durable fact, preference, pattern, decision, or project context.
|
|
16
|
+
- `memory_extract` — Prepare memories from the current conversation. Follow its returned
|
|
17
|
+
review-and-confirm instructions and wait for user approval before calling `memory_add`.
|
|
18
|
+
|
|
19
|
+
Search before answering questions about the user's preferences, projects, prior decisions,
|
|
20
|
+
recurring workflows, or earlier bugs. Save useful non-obvious outcomes and explicit
|
|
21
|
+
"remember this" requests as standalone statements with enough context for another agent.
|
|
22
|
+
|
|
23
|
+
Never store plaintext tokens, API keys, bearer credentials, or private keys in memory. Store
|
|
24
|
+
secrets in Vault and remember only an exact `clawdi://` reference when useful.
|
|
9
25
|
|
|
10
26
|
## Sessions
|
|
11
27
|
|
|
@@ -16,6 +32,33 @@ Call `session_read` when the user provides a Clawdi share URL. When the user ref
|
|
|
16
32
|
past conversation without a UUID, call `session_search` first and then read the matching
|
|
17
33
|
session. Do not use a generic web fetcher for Clawdi share URLs.
|
|
18
34
|
|
|
35
|
+
## Projects
|
|
36
|
+
|
|
37
|
+
- `project_current` — Read the runtime-bound Project.
|
|
38
|
+
- `project_list` — List Projects visible to the caller.
|
|
39
|
+
- `project_get` — Read one visible Project by UUID.
|
|
40
|
+
|
|
41
|
+
A Hosted runtime is restricted to its bound Project. Treat not-found as an access boundary
|
|
42
|
+
as well as a possible unknown UUID; do not try to bypass it through another tool.
|
|
43
|
+
|
|
44
|
+
## Vault Metadata
|
|
45
|
+
|
|
46
|
+
- `vault_list` — List attached Vaults and key counts for visible Projects.
|
|
47
|
+
- `vault_get` — List key names, provenance, and exact references for an attached Vault.
|
|
48
|
+
|
|
49
|
+
Use `vault_resolve` only when the current task requires one referenced plaintext value. Pass
|
|
50
|
+
the exact Project-scoped reference. Treat the result as sensitive: never echo it, save it to
|
|
51
|
+
Memory, or include it in logs.
|
|
52
|
+
|
|
53
|
+
The metadata tools never return plaintext secret values. Preserve exact references for
|
|
54
|
+
`vault_resolve` or when passing them to an authorized runtime:
|
|
55
|
+
|
|
56
|
+
- `clawdi://project/<project-id>/vault/<vault>/field/<field>`
|
|
57
|
+
- `clawdi://project/<project-id>/vault/<vault>/section/<section>/field/<field>`
|
|
58
|
+
|
|
59
|
+
Vault mutation is not an Agent MCP capability. Ask the user to manage Vault data through an
|
|
60
|
+
authorized human-facing surface; never call raw HTTP or invent an unavailable tool.
|
|
61
|
+
|
|
19
62
|
## Connectors
|
|
20
63
|
|
|
21
64
|
Use the Composio Tool Router meta-tools returned by `tools/list` on the `clawdi` MCP server.
|