@vorim/sdk 3.4.3 → 3.6.0
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/CHANGELOG.md +35 -0
- package/README.md +66 -0
- package/dist/index.cjs +171 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +166 -1
- package/dist/index.d.ts +166 -1
- package/dist/index.js +170 -8
- package/dist/index.js.map +1 -1
- package/dist/integrations/anthropic.cjs +25 -5
- package/dist/integrations/anthropic.cjs.map +1 -1
- package/dist/integrations/anthropic.d.cts +13 -0
- package/dist/integrations/anthropic.d.ts +13 -0
- package/dist/integrations/anthropic.js +25 -5
- package/dist/integrations/anthropic.js.map +1 -1
- package/dist/integrations/langchain.cjs +3 -4
- package/dist/integrations/langchain.cjs.map +1 -1
- package/dist/integrations/langchain.js +3 -4
- package/dist/integrations/langchain.js.map +1 -1
- package/dist/integrations/llamaindex.cjs +3 -4
- package/dist/integrations/llamaindex.cjs.map +1 -1
- package/dist/integrations/llamaindex.js +3 -4
- package/dist/integrations/llamaindex.js.map +1 -1
- package/dist/integrations/openai.cjs +37 -11
- package/dist/integrations/openai.cjs.map +1 -1
- package/dist/integrations/openai.d.cts +20 -0
- package/dist/integrations/openai.d.ts +20 -0
- package/dist/integrations/openai.js +37 -11
- package/dist/integrations/openai.js.map +1 -1
- package/package.json +2 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Changelog — @vorim/sdk
|
|
2
|
+
|
|
3
|
+
## 3.5.0
|
|
4
|
+
|
|
5
|
+
### Added — Runtime Control (gate actions before they happen)
|
|
6
|
+
|
|
7
|
+
- **`beforeAction(input, { throwOnDeny })`** — call `POST /v1/runtime/decisions`
|
|
8
|
+
before an agent performs an action and get a typed `RuntimeDecision`
|
|
9
|
+
(`allow` / `deny` / `modify` / `escalate` / `fallback`). Throws
|
|
10
|
+
`VorimDeniedError` on `deny` by default (deny is in the body, not the HTTP
|
|
11
|
+
status, so without this consumers would treat a denial as success).
|
|
12
|
+
- **`waitForDecisionResolution(decisionId, { timeoutMs, pollIntervalMs })`** —
|
|
13
|
+
poll an `escalate` decision until a human operator resolves it. The resolved
|
|
14
|
+
verdict is translated for you (`approved → allow`, `denied → deny`) and the
|
|
15
|
+
raw outcome is exposed as `escalationResolution`. Throws `ESCALATION_TIMEOUT`
|
|
16
|
+
(408) if still pending at timeout.
|
|
17
|
+
- **`VorimDeniedError`** — carries the full `RuntimeDecision` (`.decision`),
|
|
18
|
+
status 403, code `DECISION_DENIED`.
|
|
19
|
+
- **`runtimeFailOpen` constructor option** (default `true`) — on a transport
|
|
20
|
+
failure (network / DNS / timeout / 5xx) `beforeAction` returns a synthetic
|
|
21
|
+
`fallback` decision so a control-plane blip doesn't block the agent. Set
|
|
22
|
+
`false` to fail closed. A reachable server returning `deny` always denies.
|
|
23
|
+
- **`emit({ ..., decision_id })`** — carry the `decisionId` from `beforeAction`
|
|
24
|
+
into the post-action audit event so it links back to the runtime decision
|
|
25
|
+
that authorised it.
|
|
26
|
+
- New exported types: `BeforeActionInput`, `RuntimeDecision`, `DecisionVerdict`.
|
|
27
|
+
|
|
28
|
+
### Notes
|
|
29
|
+
|
|
30
|
+
- `modify` is **client-cooperative**: the SDK returns the sanitised
|
|
31
|
+
`modifiedPayload`, but your agent must send it. The platform does not sit
|
|
32
|
+
inline. Carry `decisionId` into `emit()` to keep the action auditable.
|
|
33
|
+
- Malformed/verdict-less decision responses fail **closed** to `deny`.
|
|
34
|
+
|
|
35
|
+
Requires an API key with the `runtime:decide` scope and a Growth+ plan.
|
package/README.md
CHANGED
|
@@ -258,6 +258,72 @@ await vorim.emitBatch([
|
|
|
258
258
|
]);
|
|
259
259
|
```
|
|
260
260
|
|
|
261
|
+
### Runtime Control (gate actions before they happen)
|
|
262
|
+
|
|
263
|
+
Ask Vorim whether an action should proceed **before** your agent performs it.
|
|
264
|
+
`beforeAction()` returns a typed decision and, by default, **throws on deny** —
|
|
265
|
+
because a denial is carried in the response body, not the HTTP status, so
|
|
266
|
+
without `throwOnDeny` you'd treat a denial as success.
|
|
267
|
+
|
|
268
|
+
```typescript
|
|
269
|
+
import { VorimDeniedError } from "@vorim/sdk";
|
|
270
|
+
|
|
271
|
+
try {
|
|
272
|
+
const decision = await vorim.beforeAction({
|
|
273
|
+
agentId: "agid_acme_a1b2c3d4", // always the public agid_* id
|
|
274
|
+
actionType: "tool_call",
|
|
275
|
+
actionTarget: "sendEmail",
|
|
276
|
+
requiredScope: "agent:communicate",
|
|
277
|
+
payload: { to: "customer@example.com", body: "..." },
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
// 'modify' verdicts hand back a sanitised payload (e.g. PII masked).
|
|
281
|
+
const payload = decision.modifiedPayload ?? { to: "customer@example.com" };
|
|
282
|
+
|
|
283
|
+
if (decision.decision === "allow" || decision.decision === "modify") {
|
|
284
|
+
await sendEmail(payload);
|
|
285
|
+
} else if (decision.decision === "escalate") {
|
|
286
|
+
// A human must approve. Poll until resolved (or timeout).
|
|
287
|
+
const resolved = await vorim.waitForDecisionResolution(decision.decisionId);
|
|
288
|
+
if (resolved.decision === "allow") await sendEmail(payload);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Link the post-action audit event back to the decision.
|
|
292
|
+
await vorim.emit({
|
|
293
|
+
agent_id: "agid_acme_a1b2c3d4",
|
|
294
|
+
event_type: "tool_call",
|
|
295
|
+
action: "sendEmail",
|
|
296
|
+
result: "success",
|
|
297
|
+
decision_id: decision.decisionId, // ← correlates audit ↔ decision
|
|
298
|
+
});
|
|
299
|
+
} catch (err) {
|
|
300
|
+
if (err instanceof VorimDeniedError) {
|
|
301
|
+
// err.decision carries the reason and decisionId.
|
|
302
|
+
console.warn("Action denied:", err.decision.reason);
|
|
303
|
+
} else {
|
|
304
|
+
throw err;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
**Verdicts:** `allow` · `deny` (throws `VorimDeniedError` by default) ·
|
|
310
|
+
`modify` (use `decision.modifiedPayload`) · `escalate` (poll
|
|
311
|
+
`waitForDecisionResolution`) · `fallback` (engine couldn't decide).
|
|
312
|
+
|
|
313
|
+
> **`modify` is client-cooperative.** Vorim returns the sanitised
|
|
314
|
+
> `modifiedPayload`; your agent must send it in place of the original. The
|
|
315
|
+
> platform does not sit inline and does not currently enforce that you do —
|
|
316
|
+
> carry `decisionId` into the matching `emit()` so the action stays auditable.
|
|
317
|
+
|
|
318
|
+
**Fail-open:** if the decision API is unreachable, `beforeAction()` returns a
|
|
319
|
+
synthetic `fallback` decision so a control-plane blip doesn't block your agent.
|
|
320
|
+
Pass `runtimeFailOpen: false` to the constructor to fail closed instead. A
|
|
321
|
+
reachable server returning `deny` always denies, regardless of this flag.
|
|
322
|
+
|
|
323
|
+
> Requires an API key with the `runtime:decide` scope and a Growth+ plan.
|
|
324
|
+
> `modify` verdicts are produced by policy rules; the rule-authoring API
|
|
325
|
+
> ships in a later release — until then rules are provisioned by Vorim.
|
|
326
|
+
|
|
261
327
|
### Trust Verification
|
|
262
328
|
|
|
263
329
|
```typescript
|
package/dist/index.cjs
CHANGED
|
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
CANONICAL_TOOL_CATALOGUE_VERSION: () => CANONICAL_TOOL_CATALOGUE_VERSION,
|
|
34
|
+
VorimDeniedError: () => VorimDeniedError,
|
|
34
35
|
VorimError: () => VorimError,
|
|
35
36
|
VorimSDK: () => VorimSDK,
|
|
36
37
|
canonicalPayloadV0: () => canonicalPayloadV0,
|
|
@@ -64,10 +65,9 @@ function jcsCanonicalise(value) {
|
|
|
64
65
|
return "[" + value.map(jcsCanonicalise).join(",") + "]";
|
|
65
66
|
}
|
|
66
67
|
if (typeof value === "object") {
|
|
67
|
-
const
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
});
|
|
68
|
+
const obj = value;
|
|
69
|
+
const keys = Object.keys(obj).filter((k) => obj[k] !== void 0).sort();
|
|
70
|
+
const parts = keys.map((k) => JSON.stringify(k) + ":" + jcsCanonicalise(obj[k]));
|
|
71
71
|
return "{" + parts.join(",") + "}";
|
|
72
72
|
}
|
|
73
73
|
throw new Error(`jcsCanonicalise: unsupported value type: ${typeof value}`);
|
|
@@ -117,8 +117,11 @@ async function prepareReplayContext(inputs) {
|
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
// src/index.ts
|
|
120
|
-
var SDK_VERSION = true ? "3.
|
|
120
|
+
var SDK_VERSION = true ? "3.6.0" : "0.0.0";
|
|
121
121
|
var USER_AGENT = `vorim-sdk/${SDK_VERSION}`;
|
|
122
|
+
function sleep(ms) {
|
|
123
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
124
|
+
}
|
|
122
125
|
function canonicalPayloadV0(event) {
|
|
123
126
|
return [
|
|
124
127
|
event.event_type,
|
|
@@ -140,6 +143,7 @@ var VorimSDK = class _VorimSDK {
|
|
|
140
143
|
autoSign;
|
|
141
144
|
canonicalForm;
|
|
142
145
|
chainEvents;
|
|
146
|
+
runtimeFailOpen;
|
|
143
147
|
/**
|
|
144
148
|
* In-memory keyring mapping agent_id -> PEM-encoded Ed25519 private key.
|
|
145
149
|
* Populated automatically by register() and registerEphemeral(), or
|
|
@@ -185,6 +189,7 @@ var VorimSDK = class _VorimSDK {
|
|
|
185
189
|
}
|
|
186
190
|
}
|
|
187
191
|
this.chainEvents = config.chainEvents ?? false;
|
|
192
|
+
this.runtimeFailOpen = config.runtimeFailOpen ?? true;
|
|
188
193
|
}
|
|
189
194
|
/**
|
|
190
195
|
* Register a previously-issued agent keypair so this SDK instance can
|
|
@@ -281,7 +286,8 @@ var VorimSDK = class _VorimSDK {
|
|
|
281
286
|
*/
|
|
282
287
|
async listAgents(params) {
|
|
283
288
|
const qs = new URLSearchParams(params).toString();
|
|
284
|
-
|
|
289
|
+
const env = await this.requestEnvelope("GET", `/agents${qs ? "?" + qs : ""}`);
|
|
290
|
+
return { agents: env.data ?? [], meta: env.meta ?? null };
|
|
285
291
|
}
|
|
286
292
|
/**
|
|
287
293
|
* Update an agent's metadata.
|
|
@@ -410,6 +416,149 @@ var VorimSDK = class _VorimSDK {
|
|
|
410
416
|
async revokeAgentDelegation(agentId, chainId) {
|
|
411
417
|
return this.delete(`/agents/${agentId}/delegations/${chainId}`);
|
|
412
418
|
}
|
|
419
|
+
// ─── Runtime Control (Strategy A) ──────────────────────────────────
|
|
420
|
+
/**
|
|
421
|
+
* Gate an agent action BEFORE it is performed. Calls
|
|
422
|
+
* `POST /v1/runtime/decisions` and returns a typed {@link RuntimeDecision}.
|
|
423
|
+
*
|
|
424
|
+
* By default (`throwOnDeny: true`) a `deny` verdict throws
|
|
425
|
+
* {@link VorimDeniedError} — deny is carried in the response body, not
|
|
426
|
+
* the HTTP status, so without this the caller would treat a denial as
|
|
427
|
+
* success. Pass `{ throwOnDeny: false }` to handle deny yourself.
|
|
428
|
+
*
|
|
429
|
+
* On a `modify` verdict, use `decision.modifiedPayload` in place of your
|
|
430
|
+
* original payload (e.g. a policy rule masked PII). This is
|
|
431
|
+
* client-cooperative: Vorim returns the sanitised payload but does not sit
|
|
432
|
+
* inline and does not enforce that you send it — carry `decisionId` into
|
|
433
|
+
* the matching {@link emit} so the action stays auditable. On `escalate`,
|
|
434
|
+
* poll {@link waitForDecisionResolution} for the human decision.
|
|
435
|
+
*
|
|
436
|
+
* Transport failures (network/DNS/timeout/5xx) respect the constructor's
|
|
437
|
+
* `runtimeFailOpen` flag: when true (default) a synthetic `fallback`
|
|
438
|
+
* decision is returned so a control-plane blip does not block the agent;
|
|
439
|
+
* when false the underlying error is re-thrown.
|
|
440
|
+
*
|
|
441
|
+
* Always pass the public `agid_*` id as `agentId`.
|
|
442
|
+
*
|
|
443
|
+
* @example
|
|
444
|
+
* const d = await vorim.beforeAction({
|
|
445
|
+
* agentId: 'agid_acme_evilbot',
|
|
446
|
+
* actionType: 'tool_call',
|
|
447
|
+
* actionTarget: 'sendEmail',
|
|
448
|
+
* requiredScope: 'agent:communicate',
|
|
449
|
+
* payload: { to: 'customer@example.com' },
|
|
450
|
+
* });
|
|
451
|
+
* if (d.decision === 'allow') await sendEmail(d.modifiedPayload ?? payload);
|
|
452
|
+
*/
|
|
453
|
+
async beforeAction(input, options = {}) {
|
|
454
|
+
const throwOnDeny = options.throwOnDeny ?? true;
|
|
455
|
+
const body = {
|
|
456
|
+
agent_id: input.agentId,
|
|
457
|
+
action_type: input.actionType,
|
|
458
|
+
action_target: input.actionTarget,
|
|
459
|
+
payload: input.payload,
|
|
460
|
+
context: input.context,
|
|
461
|
+
required_scope: input.requiredScope,
|
|
462
|
+
idempotency_key: input.idempotencyKey
|
|
463
|
+
};
|
|
464
|
+
let raw;
|
|
465
|
+
try {
|
|
466
|
+
raw = await this.post("/runtime/decisions", body);
|
|
467
|
+
} catch (err) {
|
|
468
|
+
const status = err instanceof VorimError ? err.status : 0;
|
|
469
|
+
const isTransport = status === 0 || status >= 500;
|
|
470
|
+
if (isTransport && this.runtimeFailOpen) {
|
|
471
|
+
return {
|
|
472
|
+
decisionId: "",
|
|
473
|
+
decision: "fallback",
|
|
474
|
+
reason: "Runtime decision API unreachable; client fail-open applied",
|
|
475
|
+
decisionRuleId: null,
|
|
476
|
+
modifiedPayload: null,
|
|
477
|
+
expiresAt: new Date(Date.now() + 6e4).toISOString(),
|
|
478
|
+
latencyMs: 0,
|
|
479
|
+
isFallback: true,
|
|
480
|
+
policyVersion: 0,
|
|
481
|
+
escalationResolution: null
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
throw err;
|
|
485
|
+
}
|
|
486
|
+
const decision = this.toRuntimeDecision(raw);
|
|
487
|
+
if (decision.decision === "deny" && throwOnDeny) {
|
|
488
|
+
throw new VorimDeniedError(decision);
|
|
489
|
+
}
|
|
490
|
+
return decision;
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
493
|
+
* Poll a decision until it leaves the `escalate` state (a human
|
|
494
|
+
* approved or denied it) or the timeout elapses. Returns the resolved
|
|
495
|
+
* decision; throws {@link VorimError} `ESCALATION_TIMEOUT` (408) if the
|
|
496
|
+
* decision is still pending when the timeout is reached.
|
|
497
|
+
*
|
|
498
|
+
* Requires an API key with the `runtime:decide` scope (the same scope
|
|
499
|
+
* `beforeAction` uses).
|
|
500
|
+
*/
|
|
501
|
+
async waitForDecisionResolution(decisionId, options = {}) {
|
|
502
|
+
const timeoutMs = options.timeoutMs ?? 3e5;
|
|
503
|
+
const pollIntervalMs = options.pollIntervalMs ?? 1e3;
|
|
504
|
+
const start = Date.now();
|
|
505
|
+
do {
|
|
506
|
+
const raw = await this.get(`/runtime/decisions/${decisionId}`);
|
|
507
|
+
if (raw?.decision !== "escalate" || raw?.escalation_resolution) {
|
|
508
|
+
return this.toRuntimeDecision(raw);
|
|
509
|
+
}
|
|
510
|
+
const remaining = timeoutMs - (Date.now() - start);
|
|
511
|
+
if (remaining <= 0) break;
|
|
512
|
+
await sleep(Math.min(pollIntervalMs, remaining));
|
|
513
|
+
} while (Date.now() - start < timeoutMs);
|
|
514
|
+
throw new VorimError(
|
|
515
|
+
408,
|
|
516
|
+
"ESCALATION_TIMEOUT",
|
|
517
|
+
`Decision ${decisionId} still pending after ${timeoutMs}ms`,
|
|
518
|
+
{ decisionId }
|
|
519
|
+
);
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* Map a snake_case decision row (from POST or GET) to the camelCase
|
|
523
|
+
* {@link RuntimeDecision}. The SDK has no generic camelizer, so the
|
|
524
|
+
* mapping is explicit — and tolerant of either the POST shape
|
|
525
|
+
* (`decision_rule_id`, `latency_ms`) or absent fields on a GET row.
|
|
526
|
+
*
|
|
527
|
+
* Escalation-resolution translation (load-bearing): the server resolves
|
|
528
|
+
* an escalation by setting `escalation_resolution` but does NOT flip the
|
|
529
|
+
* row's `decision` column off `'escalate'`. If we returned that verbatim,
|
|
530
|
+
* a caller's obvious `if (d.decision === 'deny')` check would never fire
|
|
531
|
+
* on a human DENIAL — a silent fail-open. So when a resolution is present
|
|
532
|
+
* we translate the verdict: approved → 'allow', denied → 'deny'. The raw
|
|
533
|
+
* resolution is also surfaced as `escalationResolution` for callers that
|
|
534
|
+
* want it explicitly.
|
|
535
|
+
*
|
|
536
|
+
* A missing verdict (malformed/truncated response) fails CLOSED to 'deny'
|
|
537
|
+
* rather than 'fallback', so the deny check fires on garbled input.
|
|
538
|
+
*/
|
|
539
|
+
toRuntimeDecision(raw) {
|
|
540
|
+
const resolution = raw?.escalation_resolution === "approved" ? "approved" : raw?.escalation_resolution === "denied" ? "denied" : null;
|
|
541
|
+
const hasVerdict = typeof raw?.decision === "string";
|
|
542
|
+
let decision;
|
|
543
|
+
if (raw?.decision === "escalate" && resolution === "approved") decision = "allow";
|
|
544
|
+
else if (raw?.decision === "escalate" && resolution === "denied") decision = "deny";
|
|
545
|
+
else if (hasVerdict) decision = raw.decision;
|
|
546
|
+
else decision = "deny";
|
|
547
|
+
return {
|
|
548
|
+
decisionId: raw?.decision_id ?? "",
|
|
549
|
+
decision,
|
|
550
|
+
reason: raw?.reason ?? (hasVerdict ? "" : "Malformed decision response (no verdict); failing closed"),
|
|
551
|
+
decisionRuleId: raw?.decision_rule_id ?? null,
|
|
552
|
+
modifiedPayload: raw?.modified_payload ?? null,
|
|
553
|
+
// Avoid '' (Date.parse('') === NaN). A verdict-less row gets an epoch
|
|
554
|
+
// timestamp so any staleness check treats it as already expired.
|
|
555
|
+
expiresAt: raw?.expires_at ?? (hasVerdict ? "" : (/* @__PURE__ */ new Date(0)).toISOString()),
|
|
556
|
+
latencyMs: raw?.latency_ms ?? 0,
|
|
557
|
+
isFallback: raw?.is_fallback ?? !hasVerdict,
|
|
558
|
+
policyVersion: raw?.policy_version ?? 0,
|
|
559
|
+
escalationResolution: resolution
|
|
560
|
+
};
|
|
561
|
+
}
|
|
413
562
|
// ─── Audit ────────────────────────────────────────────────────────
|
|
414
563
|
/**
|
|
415
564
|
* Emit an audit event for an agent action.
|
|
@@ -686,7 +835,12 @@ var VorimSDK = class _VorimSDK {
|
|
|
686
835
|
async delete(path) {
|
|
687
836
|
return this.request("DELETE", path);
|
|
688
837
|
}
|
|
689
|
-
|
|
838
|
+
/** Like request() but returns the FULL { data, meta, ... } envelope
|
|
839
|
+
* instead of unwrapping to `data`. Used where meta (pagination) matters. */
|
|
840
|
+
async requestEnvelope(method, path, body) {
|
|
841
|
+
return this.request(method, path, body, false);
|
|
842
|
+
}
|
|
843
|
+
async request(method, path, body, unwrap = true) {
|
|
690
844
|
const controller = new AbortController();
|
|
691
845
|
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
692
846
|
try {
|
|
@@ -710,7 +864,7 @@ var VorimSDK = class _VorimSDK {
|
|
|
710
864
|
);
|
|
711
865
|
}
|
|
712
866
|
const json = await response.json();
|
|
713
|
-
return json.data;
|
|
867
|
+
return unwrap ? json.data : json;
|
|
714
868
|
} finally {
|
|
715
869
|
clearTimeout(timeoutId);
|
|
716
870
|
}
|
|
@@ -745,12 +899,21 @@ var VorimError = class extends Error {
|
|
|
745
899
|
code;
|
|
746
900
|
details;
|
|
747
901
|
};
|
|
902
|
+
var VorimDeniedError = class extends VorimError {
|
|
903
|
+
constructor(decision) {
|
|
904
|
+
super(403, "DECISION_DENIED", decision.reason, { decisionId: decision.decisionId });
|
|
905
|
+
this.decision = decision;
|
|
906
|
+
this.name = "VorimDeniedError";
|
|
907
|
+
}
|
|
908
|
+
decision;
|
|
909
|
+
};
|
|
748
910
|
function createVorim(config) {
|
|
749
911
|
return new VorimSDK(config);
|
|
750
912
|
}
|
|
751
913
|
// Annotate the CommonJS export names for ESM import in node:
|
|
752
914
|
0 && (module.exports = {
|
|
753
915
|
CANONICAL_TOOL_CATALOGUE_VERSION,
|
|
916
|
+
VorimDeniedError,
|
|
754
917
|
VorimError,
|
|
755
918
|
VorimSDK,
|
|
756
919
|
canonicalPayloadV0,
|