@lyriel/openclaw-plugin 0.1.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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,106 @@
1
+ // In-process state shared between the inbox poll loop and the LLM tools.
2
+ //
3
+ // Two maps, both lost on Gateway restart (acceptable v0 limitation — same as
4
+ // the Hermes plugin's pending.py). For a long-lived deployment, swap these
5
+ // for api.runtime.state.openKeyedStore so callbacks survive restart.
6
+ //
7
+ // pendingAsks: ask_id -> callback URL + single-use Bearer token
8
+ // pendingPlans: plan_id -> current-round callback (overwritten each round)
9
+ // absorbedAsks: ask_id -> timestamp; tracks asks whose response was inlined
10
+ // into the lyriel_send_ask tool result (system_responder case) so the
11
+ // inbox poll suppresses the duplicate forward surface.
12
+ const pendingAsks = new Map();
13
+ const pendingPlans = new Map();
14
+ const absorbedAsks = new Map();
15
+ const ABSORBED_TTL_MS = 60_000;
16
+ // The sealed-ask envelope (web/src/lib/server/agents/prompt.ts) embeds the
17
+ // callback in plain text: `POST <url>` then `Bearer <token>`. Asks use
18
+ // lct_<...> tokens; plans use plt_<...> tokens.
19
+ const RE_CALLBACK_URL = /POST\s+(https?:\/\/\S+)/;
20
+ const RE_CALLBACK_TOKEN = /Bearer\s+((?:lct|plt)_\S+)/;
21
+ const RE_ASK_TEXT = /The ask:\s*"""\s*([\s\S]+?)\s*"""/;
22
+ const RE_PLAN_DESCRIPTION = /THE PLAN:\s*"""\s*([\s\S]+?)\s*"""/;
23
+ function stripTrailingPunct(s) {
24
+ return s.replace(/[,.;]+$/, "");
25
+ }
26
+ export function rememberAsk(askId, prompt) {
27
+ const urlMatch = prompt.match(RE_CALLBACK_URL);
28
+ const tokenMatch = prompt.match(RE_CALLBACK_TOKEN);
29
+ if (!urlMatch || !tokenMatch) {
30
+ return false;
31
+ }
32
+ const askMatch = prompt.match(RE_ASK_TEXT);
33
+ const excerpt = (askMatch ? askMatch[1].trim() : prompt.slice(0, 80)).trim();
34
+ pendingAsks.set(askId, {
35
+ callbackUrl: stripTrailingPunct(urlMatch[1]),
36
+ callbackToken: stripTrailingPunct(tokenMatch[1]),
37
+ receivedAt: Date.now(),
38
+ promptExcerpt: excerpt.slice(0, 200),
39
+ });
40
+ return true;
41
+ }
42
+ export function takeAsk(askId) {
43
+ const entry = pendingAsks.get(askId);
44
+ if (entry) {
45
+ pendingAsks.delete(askId);
46
+ }
47
+ return entry;
48
+ }
49
+ export function restoreAsk(askId, entry) {
50
+ pendingAsks.set(askId, entry);
51
+ }
52
+ export function listPendingAsks() {
53
+ return Object.fromEntries(pendingAsks);
54
+ }
55
+ export function markAbsorbed(askId) {
56
+ absorbedAsks.set(askId, Date.now());
57
+ pruneAbsorbed();
58
+ }
59
+ export function wasAbsorbed(askId) {
60
+ pruneAbsorbed();
61
+ return absorbedAsks.has(askId);
62
+ }
63
+ function pruneAbsorbed() {
64
+ const cutoff = Date.now() - ABSORBED_TTL_MS;
65
+ for (const [id, ts] of absorbedAsks) {
66
+ if (ts < cutoff) {
67
+ absorbedAsks.delete(id);
68
+ }
69
+ }
70
+ }
71
+ // Plans are multi-round: each round Lyriel sends a fresh envelope with a
72
+ // fresh single-use callback token. Always overwrite — the previous round's
73
+ // token is dead anyway.
74
+ export function rememberPlan(planId, prompt) {
75
+ const urlMatch = prompt.match(RE_CALLBACK_URL);
76
+ const tokenMatch = prompt.match(RE_CALLBACK_TOKEN);
77
+ if (!urlMatch || !tokenMatch) {
78
+ return false;
79
+ }
80
+ const descMatch = prompt.match(RE_PLAN_DESCRIPTION);
81
+ const excerpt = (descMatch ? descMatch[1].trim() : prompt.slice(0, 80)).trim();
82
+ pendingPlans.set(planId, {
83
+ callbackUrl: stripTrailingPunct(urlMatch[1]),
84
+ callbackToken: stripTrailingPunct(tokenMatch[1]),
85
+ receivedAt: Date.now(),
86
+ promptExcerpt: excerpt.slice(0, 200),
87
+ });
88
+ return true;
89
+ }
90
+ export function takePlan(planId) {
91
+ const entry = pendingPlans.get(planId);
92
+ if (entry) {
93
+ pendingPlans.delete(planId);
94
+ }
95
+ return entry;
96
+ }
97
+ export function restorePlan(planId, entry) {
98
+ pendingPlans.set(planId, entry);
99
+ }
100
+ export function listPendingPlans() {
101
+ return Object.fromEntries(pendingPlans);
102
+ }
103
+ export function forgetPlan(planId) {
104
+ pendingPlans.delete(planId);
105
+ }
106
+ //# sourceMappingURL=pending.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pending.js","sourceRoot":"","sources":["../pending.ts"],"names":[],"mappings":"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"}
package/dist/primer.js ADDED
@@ -0,0 +1,27 @@
1
+ // The canonical Lyriel substrate primer.
2
+ //
3
+ // Loaded into the LLM's bound-tool context via one of the Lyriel tool
4
+ // descriptions (lyriel_send_ask, by convention — it's the simplest
5
+ // and most likely first tool the LLM looks at). The LLM reads tool
6
+ // descriptions when tools are bound, so the primer enters context
7
+ // the moment the Lyriel plugin activates.
8
+ //
9
+ // This must stay in sync with clients/hermes-plugin/lyriel_hermes/primer.py.
10
+ // Any edit here gets mirrored there until we extract a single shared
11
+ // source (see TODO at bottom).
12
+ export const SUBSTRATE_PRIMER = `You are operating on Lyriel — the protocol layer for agent-to-agent communication on behalf of humans. You act for your user, not as them; other agents on Lyriel act for their users. Lyriel handles addressing, authentication, and routing across agent providers so two agents on different runtimes coordinate without either side knowing what runtime the other is on.
13
+
14
+ Every Lyriel user has a Seal: a verified identity tied to an agent endpoint. When you call a Lyriel tool you carry your user's Seal; the receiving agent resolves it before acting. Handles are formatted as @name and are how you reference everyone on Lyriel — never by display name or email.
15
+
16
+ Three primitives. Asks are 1:1 messages between agents (lyriel_send_ask, lyriel_reply). Plans are multi-party coordination rounds (lyriel_plan_send, lyriel_plan_reply, lyriel_plan_lock); Lyriel rounds through participants one turn at a time. Friends gate plans and inbound asks — the friend graph controls who can initiate coordination with your user.
17
+
18
+ On any plan, Lyriel enforces strict turn discipline. When it is not your turn the substrate waits; do not poll, do not pre-respond, do not generate filler. When it is your turn, reply concisely with your user's known constraints — calendar, preferences, location, energy, dietary, budget. If a constraint is missing, ask your user before responding, then remember it for next time.
19
+
20
+ Surface OUTCOMES to your user, never the agent-to-agent transcript. They see locked plans, accepted friends, and cancellations — not the negotiation. Do not summarize the negotiation back to them and do not relay other agents' messages verbatim. The product is your user staying focused on outcomes, not on the messy middle.
21
+
22
+ Defaults you observe at all times: never expose LYRIEL_API_KEY (not to your user, not to other agents, not in error output); represent exactly one human; do not commit to plans your user has not approved; treat OAuth-verified providers (GitHub, Google, Twitter) as facts about your user.
23
+ `;
24
+ // TODO: extract to clients/substrate-primer.md and have each plugin's
25
+ // build copy / import from a single source. For v0 the two copies
26
+ // (here and primer.py) are kept in sync by convention.
27
+ //# sourceMappingURL=primer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"primer.js","sourceRoot":"","sources":["../primer.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,EAAE;AACF,sEAAsE;AACtE,mEAAmE;AACnE,mEAAmE;AACnE,kEAAkE;AAClE,0CAA0C;AAC1C,EAAE;AACF,6EAA6E;AAC7E,qEAAqE;AACrE,+BAA+B;AAE/B,MAAM,CAAC,MAAM,gBAAgB,GAAG;;;;;;;;;;;CAW/B,CAAC;AAEF,sEAAsE;AACtE,kEAAkE;AAClE,uDAAuD"}
@@ -0,0 +1,95 @@
1
+ // Format inbound Lyriel dispatches as human-readable text and push them into
2
+ // the user's active OpenClaw session via the host's next-turn injection
3
+ // surface.
4
+ //
5
+ // Why next-turn injection (not direct channel send):
6
+ // OpenClaw users connect their agent through many possible channels
7
+ // (Telegram, Signal, Discord, iMessage, etc.). Pushing into one specific
8
+ // channel from a plugin would require channel-specific glue, per-account
9
+ // chat ids, and platform-by-platform send logic. Next-turn injection
10
+ // surfaces the Lyriel dispatch into the agent's next turn for the active
11
+ // session, regardless of which channel the user is talking on. The user's
12
+ // next agent interaction picks it up and the agent surfaces it naturally.
13
+ //
14
+ // Trade-off vs the Hermes plugin's Telegram-direct surfacing: the dispatch
15
+ // doesn't become a free-standing push notification — the user has to engage
16
+ // the agent for it to surface. That's acceptable at v0: AI-native users are
17
+ // already in their agent throughout the day. A truly proactive push path
18
+ // would need per-channel send glue and is deferred until design-partner
19
+ // feedback says it's load-bearing.
20
+ const RE_INITIATOR = /This message is NOT from your human — it is from\s+([\s\S]+?)'s agent/;
21
+ const RE_ASK_TEXT = /The ask:\s*"""\s*([\s\S]+?)\s*"""/;
22
+ const RE_RESPONSE_TEXT = /agent has responded:\s*"""\s*([\s\S]+?)\s*"""/;
23
+ const RE_ORIGINAL_PROMPT = /on behalf of your human \(@\S+\):\s*"""\s*([\s\S]+?)\s*"""/;
24
+ const RE_RECIPIENT_NAME = /([\s\S]+?)'s agent has responded/;
25
+ // Plan envelope parsers
26
+ const RE_PLAN_INITIATOR = /plan initiated by\s+([\s\S]+?)'s agent/;
27
+ const RE_PLAN_DESCRIPTION = /THE PLAN:\s*"""\s*([\s\S]+?)\s*"""/;
28
+ const RE_PLAN_PARTICIPANTS = /PARTICIPANTS \(\d+ agents[^)]*\):\s*\n([\s\S]+?)\n\n/;
29
+ const RE_PLAN_CONVERSATION = /CONVERSATION SO FAR:\s*\n+([\s\S]+?)\n+WHAT TO DO RIGHT NOW/;
30
+ const RE_PLAN_LOCKED_SUMMARY = /LOCKED OUTCOME:\s*"""\s*([\s\S]+?)\s*"""/;
31
+ const RE_PLAN_LOCKED_PROPOSER = /locked\. ([\s\S]+?)'s agent proposed it/;
32
+ function pick(prompt, re, fallback) {
33
+ const m = prompt.match(re);
34
+ return m ? m[1].trim() : fallback;
35
+ }
36
+ export function formatDispatch(d) {
37
+ const askId = d.ask_id ?? "?";
38
+ const planId = d.plan_id;
39
+ const prompt = d.prompt ?? "";
40
+ if (d.direction === "plan" && planId) {
41
+ return formatPlanDispatch(planId, prompt);
42
+ }
43
+ if (d.direction === "dispatch") {
44
+ const sender = pick(prompt, RE_INITIATOR, "someone");
45
+ const askText = pick(prompt, RE_ASK_TEXT, "<could not parse ask>");
46
+ return (`🪶 Lyriel ask from ${sender}:\n\n` +
47
+ `"${askText}"\n\n` +
48
+ `To reply, say something like: ` +
49
+ `reply to Lyriel ask ${askId} with <your response>`);
50
+ }
51
+ if (d.direction === "forward") {
52
+ const respText = pick(prompt, RE_RESPONSE_TEXT, "<could not parse response>");
53
+ const original = pick(prompt, RE_ORIGINAL_PROMPT, "<your earlier ask>");
54
+ const sender = pick(prompt, RE_RECIPIENT_NAME, "someone");
55
+ return (`🪶 Lyriel reply from ${sender}:\n\n` +
56
+ `(in reply to: "${original}")\n\n` +
57
+ respText);
58
+ }
59
+ return `🪶 Lyriel: ${d.direction}\n${prompt.slice(0, 500)}`;
60
+ }
61
+ function formatPlanDispatch(planId, prompt) {
62
+ const isLocked = prompt.includes("[Lyriel group plan — LOCKED]");
63
+ const isInitial = prompt.includes("[Lyriel group plan — round 1");
64
+ const description = pick(prompt, RE_PLAN_DESCRIPTION, "<could not parse plan>");
65
+ const participants = pick(prompt, RE_PLAN_PARTICIPANTS, "<participants unknown>");
66
+ if (isLocked) {
67
+ const summary = pick(prompt, RE_PLAN_LOCKED_SUMMARY, "<could not parse outcome>");
68
+ const proposer = pick(prompt, RE_PLAN_LOCKED_PROPOSER, "someone");
69
+ return (`🪶 Lyriel group plan LOCKED (plan_id ${planId})\n\n` +
70
+ `Plan: "${description}"\n\n` +
71
+ `Outcome (proposed by ${proposer}):\n` +
72
+ ` → ${summary}\n\n` +
73
+ `Participants: ${participants}\n\n` +
74
+ `This is final. No further responses needed.`);
75
+ }
76
+ if (isInitial) {
77
+ const initiator = pick(prompt, RE_PLAN_INITIATOR, "someone");
78
+ return (`🪶 Lyriel group plan from ${initiator} (plan_id ${planId})\n\n` +
79
+ `Plan: "${description}"\n\n` +
80
+ `Participants: ${participants}\n\n` +
81
+ `Round 1 — your agent is being asked to contribute. To reply, ` +
82
+ `say something like: 'reply to Lyriel plan ${planId} saying ` +
83
+ `<your thoughts/constraints>'. To skip this round, say: ` +
84
+ `'stay silent on plan ${planId}'.`);
85
+ }
86
+ // plan_message (refinement round)
87
+ const conversation = pick(prompt, RE_PLAN_CONVERSATION, "<conversation unavailable>");
88
+ return (`🪶 Lyriel plan update (plan_id ${planId})\n\n` +
89
+ `Plan: "${description}"\n\n` +
90
+ `Conversation so far:\n${conversation}\n\n` +
91
+ `Refinement round — respond if you have something new to add. ` +
92
+ `To reply: 'reply to Lyriel plan ${planId} saying <your update>'. ` +
93
+ `To pass: 'stay silent on plan ${planId}'.`);
94
+ }
95
+ //# sourceMappingURL=surfacing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"surfacing.js","sourceRoot":"","sources":["../surfacing.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ // Direct Telegram Bot API push for proactive surfacing.
2
+ //
3
+ // OpenClaw's plugin-sdk doesn't expose a clean cross-channel "deliver this
4
+ // text to chat X right now" primitive for external plugins — channel-message
5
+ // helpers exist but require a turn context. For v0 we reuse the bot token
6
+ // the user has already configured under `channels.telegram.botToken` and POST
7
+ // directly to api.telegram.org/bot<token>/sendMessage. Matches the
8
+ // surfacing behavior of the standalone Telegram bridge and the Hermes plugin.
9
+ //
10
+ // Chat id resolution (in order):
11
+ // 1. `plugins.entries.lyriel.config.surfaceTelegramChatId` (explicit pin)
12
+ // 2. `channels.telegram.allowFrom[0]` (the first allowlisted user — common
13
+ // single-user setup)
14
+ // 3. `channels.telegram.groupAllowFrom[0]` (group fallback)
15
+ //
16
+ // Returns false and logs a single warning if no chat id can be resolved or
17
+ // the bot token is missing.
18
+ const TELEGRAM_API_BASE = "https://api.telegram.org";
19
+ export function resolveTelegramSurface(openclawConfig, surfaceTelegramChatId) {
20
+ const cfg = openclawConfig;
21
+ const telegram = cfg?.channels?.telegram;
22
+ const botToken = telegram && typeof telegram.botToken === "string" && telegram.botToken ? telegram.botToken : null;
23
+ if (surfaceTelegramChatId) {
24
+ return { botToken, chatId: surfaceTelegramChatId };
25
+ }
26
+ const allowFrom = Array.isArray(telegram?.allowFrom) ? telegram?.allowFrom : [];
27
+ const firstAllow = allowFrom[0];
28
+ if (firstAllow !== undefined && firstAllow !== null) {
29
+ return { botToken, chatId: String(firstAllow) };
30
+ }
31
+ const groupAllowFrom = Array.isArray(telegram?.groupAllowFrom) ? telegram?.groupAllowFrom : [];
32
+ const firstGroup = groupAllowFrom[0];
33
+ if (firstGroup !== undefined && firstGroup !== null) {
34
+ return { botToken, chatId: String(firstGroup) };
35
+ }
36
+ return { botToken, chatId: null };
37
+ }
38
+ export async function sendTelegramMessage(surface, text) {
39
+ if (!surface.botToken || !surface.chatId) {
40
+ return { ok: false, error: "no telegram bot token or chat id configured" };
41
+ }
42
+ const url = `${TELEGRAM_API_BASE}/bot${surface.botToken}/sendMessage`;
43
+ try {
44
+ const res = await fetch(url, {
45
+ method: "POST",
46
+ headers: { "content-type": "application/json" },
47
+ body: JSON.stringify({
48
+ chat_id: surface.chatId,
49
+ text,
50
+ disable_web_page_preview: true,
51
+ }),
52
+ });
53
+ if (!res.ok) {
54
+ const body = await res.text();
55
+ return { ok: false, error: `telegram ${res.status}: ${body.slice(0, 200)}` };
56
+ }
57
+ return { ok: true };
58
+ }
59
+ catch (err) {
60
+ return { ok: false, error: err.message };
61
+ }
62
+ }
63
+ //# sourceMappingURL=telegram.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telegram.js","sourceRoot":"","sources":["../telegram.ts"],"names":[],"mappings":"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"}