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.
@@ -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 == "secretRefEquals":
428
+ if matcher_type in {"secretRefEquals", "secretRefPrefix"}:
411
429
  secret = secrets.get(str(matcher.get("secretRef", "")))
412
- return secret is not None and value == f"{matcher.get('prefix', '')}{secret}"
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawdi",
3
- "version": "0.13.28",
3
+ "version": "0.13.30",
4
4
  "description": "iCloud for AI Agents — cross-agent sessions, skills, memory, and vault.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -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 CLI
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
- - Use `clawdi vault set KEY --prompt` for one-off manual secret entry; it prompts without echoing input.
120
- - Use `clawdi vault set KEY --stdin` for piped values. Empty stdin is rejected unless `--allow-empty` is passed intentionally.
121
- - Use `clawdi vault set KEY --value <value>` only when shell history exposure is acceptable.
122
- - Use service-specific vault slugs such as `api-service/env/KEY`; avoid broad slugs such as `prod/KEY`.
123
- - Use `clawdi vault import --vault <service-slug> --section <name> --project <project> --yes <file>` for non-interactive `.env` migrations into a section.
124
- - Keep `.env` import keys as POSIX environment identifiers such as `OPENAI_API_KEY`; section names belong in `--section`, not inside the key name.
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 CLI
157
+ ## AI Provider Management
131
158
 
132
- When the user asks to configure model providers, API keys, or Codex OAuth for agents, use `clawdi ai-provider`:
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
- - Add reusable providers with `clawdi ai-provider add <id> --type <openai|anthropic|openrouter|gemini|mistral|custom_openai_compatible> --default-model <model> --auth <env:KEY|clawdi://...|agent:codex/profile|none>`.
135
- - Validate metadata with `clawdi ai-provider validate [provider-id]`.
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: "Find and read the user's past agent sessions across Claude Code, Codex, OpenClaw, and Hermes; use connected-service tools such as Gmail, GitHub, Notion, Drive, and Calendar; and read Clawdi share URLs (https://cloud.clawdi.ai/s/...) the user pastes."
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.