@rubytech/create-realagent-code 0.1.594 → 0.1.595

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/create-realagent-code",
3
- "version": "0.1.594",
3
+ "version": "0.1.595",
4
4
  "description": "Install Real Agent — Built for agents. By agents.",
5
5
  "bin": {
6
6
  "create-realagent-code": "./dist/index.js"
@@ -0,0 +1,19 @@
1
+ /** Matches CARD_TTL_MS in the telegram MCP's card store. A label outlives its
2
+ * card by no useful margin, and without a ceiling every callback data string
3
+ * ever sent is kept for ever. */
4
+ export declare const BUTTON_LABEL_TTL_MS: number;
5
+ /** Scoped by bot AND chat, not by the data alone: two chats can use the same
6
+ * callback data for different words. */
7
+ export declare function buttonLabelKey(botId: string, chatId: string | number, data: string): string;
8
+ /** Record every button on one keyboard, and drop every entry gone
9
+ * BUTTON_LABEL_TTL_MS untouched. Compaction rides the write because the write
10
+ * already rewrites the whole file. An empty list writes nothing: a card with no
11
+ * buttons must not create the file. */
12
+ export declare function writeButtonLabels(storeDir: string, entries: Array<{
13
+ key: string;
14
+ label: string;
15
+ }>, now?: number): void;
16
+ /** The stored wording for this key, or null. Null past the TTL, so a caller
17
+ * falls back to the raw data rather than showing a label for a card long gone. */
18
+ export declare function readButtonLabel(storeDir: string, key: string, now?: number): string | null;
19
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAkBA;;kCAEkC;AAClC,eAAO,MAAM,mBAAmB,QAA0B,CAAC;AAa3D;yCACyC;AACzC,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE3F;AAoBD;;;wCAGwC;AACxC,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,KAAK,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,EAC9C,GAAG,GAAE,MAAmB,GACvB,IAAI,CASN;AAED;mFACmF;AACnF,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,GAAG,GAAE,MAAmB,GACvB,MAAM,GAAG,IAAI,CAIf"}
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BUTTON_LABEL_TTL_MS = void 0;
4
+ exports.buttonLabelKey = buttonLabelKey;
5
+ exports.writeButtonLabels = writeButtonLabels;
6
+ exports.readButtonLabel = readButtonLabel;
7
+ // Task 2673 — what a button SAID, keyed by what a press CARRIES.
8
+ //
9
+ // A press delivers only its callback data, so an operator reading the
10
+ // conversation sees `[button: pax_1]` and not "1 passenger". The wording exists
11
+ // once, in the keyboard the card tool sent, and was never persisted.
12
+ //
13
+ // This is a shared platform library rather than a key inside the telegram MCP's
14
+ // own card store because the two sides live in different processes: the plugin
15
+ // writes it and the webhook route reads it, and the route imports from
16
+ // platform/lib and never from platform/plugins.
17
+ //
18
+ // Keyed on the callback data rather than the card key: the route holds botId,
19
+ // chatId and data, and never the caller's key.
20
+ const node_fs_1 = require("node:fs");
21
+ const node_path_1 = require("node:path");
22
+ const FILE = "telegram-button-labels.json";
23
+ /** Matches CARD_TTL_MS in the telegram MCP's card store. A label outlives its
24
+ * card by no useful margin, and without a ceiling every callback data string
25
+ * ever sent is kept for ever. */
26
+ exports.BUTTON_LABEL_TTL_MS = 7 * 24 * 60 * 60 * 1000;
27
+ function isEntry(v) {
28
+ if (!v || typeof v !== "object" || Array.isArray(v))
29
+ return false;
30
+ const e = v;
31
+ return typeof e.label === "string" && typeof e.at === "number" && Number.isFinite(e.at);
32
+ }
33
+ /** Scoped by bot AND chat, not by the data alone: two chats can use the same
34
+ * callback data for different words. */
35
+ function buttonLabelKey(botId, chatId, data) {
36
+ return `${botId}:${chatId}:${data}`;
37
+ }
38
+ function readAll(path) {
39
+ if (!(0, node_fs_1.existsSync)(path))
40
+ return {};
41
+ try {
42
+ const parsed = JSON.parse((0, node_fs_1.readFileSync)(path, "utf-8"));
43
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
44
+ return {};
45
+ const out = {};
46
+ for (const [k, v] of Object.entries(parsed)) {
47
+ if (isEntry(v))
48
+ out[k] = v;
49
+ }
50
+ return out;
51
+ }
52
+ catch {
53
+ // Unparseable state reads as "no label", which renders the raw data exactly
54
+ // as it did before this library existed. Throwing here would take a webhook
55
+ // down over a file no caller sees.
56
+ return {};
57
+ }
58
+ }
59
+ /** Record every button on one keyboard, and drop every entry gone
60
+ * BUTTON_LABEL_TTL_MS untouched. Compaction rides the write because the write
61
+ * already rewrites the whole file. An empty list writes nothing: a card with no
62
+ * buttons must not create the file. */
63
+ function writeButtonLabels(storeDir, entries, now = Date.now()) {
64
+ if (entries.length === 0)
65
+ return;
66
+ const path = (0, node_path_1.join)(storeDir, FILE);
67
+ const kept = {};
68
+ for (const [key, entry] of Object.entries(readAll(path))) {
69
+ if (now - entry.at < exports.BUTTON_LABEL_TTL_MS)
70
+ kept[key] = entry;
71
+ }
72
+ for (const e of entries)
73
+ kept[e.key] = { label: e.label, at: now };
74
+ (0, node_fs_1.writeFileSync)(path, JSON.stringify(kept, null, 2) + "\n", "utf-8");
75
+ }
76
+ /** The stored wording for this key, or null. Null past the TTL, so a caller
77
+ * falls back to the raw data rather than showing a label for a card long gone. */
78
+ function readButtonLabel(storeDir, key, now = Date.now()) {
79
+ const entry = readAll((0, node_path_1.join)(storeDir, FILE))[key];
80
+ if (!entry)
81
+ return null;
82
+ return now - entry.at >= exports.BUTTON_LABEL_TTL_MS ? null : entry.label;
83
+ }
84
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,67 @@
1
+ import { describe, expect, it, beforeEach, afterEach } from 'vitest'
2
+ import { mkdtempSync, rmSync, existsSync, readFileSync } from 'node:fs'
3
+ import { join } from 'node:path'
4
+ import { tmpdir } from 'node:os'
5
+ import {
6
+ BUTTON_LABEL_TTL_MS,
7
+ buttonLabelKey,
8
+ readButtonLabel,
9
+ writeButtonLabels,
10
+ } from '../index.js'
11
+
12
+ let dir: string
13
+
14
+ beforeEach(() => {
15
+ dir = mkdtempSync(join(tmpdir(), 'tg-labels-'))
16
+ })
17
+
18
+ afterEach(() => {
19
+ rmSync(dir, { recursive: true, force: true })
20
+ })
21
+
22
+ describe('button label store', () => {
23
+ it('keys on bot, chat and callback data, which is all a press carries', () => {
24
+ expect(buttonLabelKey('111', 999, 'pax_1')).toBe('111:999:pax_1')
25
+ expect(buttonLabelKey('111', '999', 'pax_1')).toBe('111:999:pax_1')
26
+ })
27
+
28
+ it('reads back a label written for that key', () => {
29
+ writeButtonLabels(dir, [{ key: buttonLabelKey('111', 999, 'pax_1'), label: '1 passenger' }])
30
+ expect(readButtonLabel(dir, buttonLabelKey('111', 999, 'pax_1'))).toBe('1 passenger')
31
+ })
32
+
33
+ it('returns null for a key never written', () => {
34
+ expect(readButtonLabel(dir, buttonLabelKey('111', 999, 'nope'))).toBeNull()
35
+ })
36
+
37
+ it('returns null past the ttl, and prunes it on the next write', () => {
38
+ const t0 = 1_000_000
39
+ const k = buttonLabelKey('111', 999, 'pax_1')
40
+ writeButtonLabels(dir, [{ key: k, label: '1 passenger' }], t0)
41
+ expect(readButtonLabel(dir, k, t0 + BUTTON_LABEL_TTL_MS)).toBeNull()
42
+ writeButtonLabels(dir, [{ key: buttonLabelKey('111', 999, 'pax_2'), label: '2 passengers' }], t0 + BUTTON_LABEL_TTL_MS)
43
+ const onDisk = JSON.parse(readFileSync(join(dir, 'telegram-button-labels.json'), 'utf-8')) as Record<string, unknown>
44
+ expect(Object.keys(onDisk)).toEqual(['111:999:pax_2'])
45
+ })
46
+
47
+ it('a later write for the same key replaces the label and refreshes its age', () => {
48
+ const t0 = 1_000_000
49
+ const k = buttonLabelKey('111', 999, 'pax_1')
50
+ writeButtonLabels(dir, [{ key: k, label: 'one' }], t0)
51
+ writeButtonLabels(dir, [{ key: k, label: 'ONE' }], t0 + 1)
52
+ expect(readButtonLabel(dir, k, t0 + 2)).toBe('ONE')
53
+ })
54
+
55
+ it('an unparseable file reads as no label and never throws', () => {
56
+ const path = join(dir, 'telegram-button-labels.json')
57
+ writeButtonLabels(dir, [{ key: 'a', label: 'b' }])
58
+ rmSync(path)
59
+ expect(readButtonLabel(dir, 'a')).toBeNull()
60
+ expect(existsSync(path)).toBe(false)
61
+ })
62
+
63
+ it('an empty entry list writes nothing, so a card with no buttons touches no file', () => {
64
+ writeButtonLabels(dir, [])
65
+ expect(existsSync(join(dir, 'telegram-button-labels.json'))).toBe(false)
66
+ })
67
+ })
@@ -0,0 +1,88 @@
1
+ // Task 2673 — what a button SAID, keyed by what a press CARRIES.
2
+ //
3
+ // A press delivers only its callback data, so an operator reading the
4
+ // conversation sees `[button: pax_1]` and not "1 passenger". The wording exists
5
+ // once, in the keyboard the card tool sent, and was never persisted.
6
+ //
7
+ // This is a shared platform library rather than a key inside the telegram MCP's
8
+ // own card store because the two sides live in different processes: the plugin
9
+ // writes it and the webhook route reads it, and the route imports from
10
+ // platform/lib and never from platform/plugins.
11
+ //
12
+ // Keyed on the callback data rather than the card key: the route holds botId,
13
+ // chatId and data, and never the caller's key.
14
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
15
+ import { join } from "node:path";
16
+
17
+ const FILE = "telegram-button-labels.json";
18
+
19
+ /** Matches CARD_TTL_MS in the telegram MCP's card store. A label outlives its
20
+ * card by no useful margin, and without a ceiling every callback data string
21
+ * ever sent is kept for ever. */
22
+ export const BUTTON_LABEL_TTL_MS = 7 * 24 * 60 * 60 * 1000;
23
+
24
+ interface LabelEntry {
25
+ label: string;
26
+ at: number;
27
+ }
28
+
29
+ function isEntry(v: unknown): v is LabelEntry {
30
+ if (!v || typeof v !== "object" || Array.isArray(v)) return false;
31
+ const e = v as Record<string, unknown>;
32
+ return typeof e.label === "string" && typeof e.at === "number" && Number.isFinite(e.at);
33
+ }
34
+
35
+ /** Scoped by bot AND chat, not by the data alone: two chats can use the same
36
+ * callback data for different words. */
37
+ export function buttonLabelKey(botId: string, chatId: string | number, data: string): string {
38
+ return `${botId}:${chatId}:${data}`;
39
+ }
40
+
41
+ function readAll(path: string): Record<string, LabelEntry> {
42
+ if (!existsSync(path)) return {};
43
+ try {
44
+ const parsed = JSON.parse(readFileSync(path, "utf-8")) as unknown;
45
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
46
+ const out: Record<string, LabelEntry> = {};
47
+ for (const [k, v] of Object.entries(parsed as Record<string, unknown>)) {
48
+ if (isEntry(v)) out[k] = v;
49
+ }
50
+ return out;
51
+ } catch {
52
+ // Unparseable state reads as "no label", which renders the raw data exactly
53
+ // as it did before this library existed. Throwing here would take a webhook
54
+ // down over a file no caller sees.
55
+ return {};
56
+ }
57
+ }
58
+
59
+ /** Record every button on one keyboard, and drop every entry gone
60
+ * BUTTON_LABEL_TTL_MS untouched. Compaction rides the write because the write
61
+ * already rewrites the whole file. An empty list writes nothing: a card with no
62
+ * buttons must not create the file. */
63
+ export function writeButtonLabels(
64
+ storeDir: string,
65
+ entries: Array<{ key: string; label: string }>,
66
+ now: number = Date.now(),
67
+ ): void {
68
+ if (entries.length === 0) return;
69
+ const path = join(storeDir, FILE);
70
+ const kept: Record<string, LabelEntry> = {};
71
+ for (const [key, entry] of Object.entries(readAll(path))) {
72
+ if (now - entry.at < BUTTON_LABEL_TTL_MS) kept[key] = entry;
73
+ }
74
+ for (const e of entries) kept[e.key] = { label: e.label, at: now };
75
+ writeFileSync(path, JSON.stringify(kept, null, 2) + "\n", "utf-8");
76
+ }
77
+
78
+ /** The stored wording for this key, or null. Null past the TTL, so a caller
79
+ * falls back to the raw data rather than showing a label for a card long gone. */
80
+ export function readButtonLabel(
81
+ storeDir: string,
82
+ key: string,
83
+ now: number = Date.now(),
84
+ ): string | null {
85
+ const entry = readAll(join(storeDir, FILE))[key];
86
+ if (!entry) return null;
87
+ return now - entry.at >= BUTTON_LABEL_TTL_MS ? null : entry.label;
88
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "dist",
5
+ "rootDir": "src"
6
+ },
7
+ "include": ["src"],
8
+ "exclude": ["src/__tests__"]
9
+ }
@@ -0,0 +1,9 @@
1
+ import { defineConfig } from "vitest/config";
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ environment: "node",
6
+ globals: false,
7
+ include: ["src/__tests__/**/*.test.ts"],
8
+ },
9
+ });
@@ -10,8 +10,8 @@
10
10
  "pdf-lib": "^1.17.1"
11
11
  },
12
12
  "scripts": {
13
- "build": "tsc -p lib/account-schema-regions/tsconfig.json && tsc -p lib/account-ingest-token/tsconfig.json && tsc -p lib/models/tsconfig.json && tsc -p lib/agent-slug/tsconfig.json && tsc -p lib/mcp-spawn-tee/tsconfig.json && tsc -p lib/mcp-lifeline/tsconfig.json && tsc -p lib/mcp-eager/tsconfig.json && tsc -p lib/account-enumeration/tsconfig.json && tsc -p lib/account-scope/tsconfig.json && tsc -p lib/shared-folder/tsconfig.json && tsc -p lib/graph-write/tsconfig.json && tsc -p lib/ledger-core/tsconfig.json && tsc -p lib/embed-client/tsconfig.json && tsc -p lib/graph-mcp/tsconfig.json && tsc -p lib/graph-trash/tsconfig.json && tsc -p lib/active-rules/tsconfig.json && tsc -p lib/admin-conversation-purge/tsconfig.json && tsc -p lib/graph-search/tsconfig.json && tsc -p lib/graph-style/tsconfig.json && tsc -p lib/device-url/tsconfig.json && tsc -p lib/brand-templating/tsconfig.json && tsc -p lib/entitlement/tsconfig.json && tsc -p lib/task-secrets/tsconfig.json && tsc -p lib/admins-write/tsconfig.json && tsc -p lib/admin-access-password/tsconfig.json && tsc -p lib/persistent-components/tsconfig.json && tsc -p lib/require-port-env/tsconfig.json && tsc -p lib/aeo-llms-txt-writer/tsconfig.json && tsc -p lib/obsidian-parser/tsconfig.json && tsc -p lib/storage-broker/tsconfig.json && tsc -p lib/routine-templates/tsconfig.json && tsc -p lib/agent-dispatch-rule/tsconfig.json && tsc -p lib/canonical-instant/tsconfig.json && tsc -p lib/dispatch-read/tsconfig.json && tsc -p lib/dispatch-write/tsconfig.json && tsc -p lib/learning-constants/tsconfig.json && tsc -p lib/telegram-media/tsconfig.json && tsc -p lib/telegram-managed-bot/tsconfig.json && tsc -p lib/telegram-reach/tsconfig.json && tsc -p services/claude-session-manager/tsconfig.json && tsc -p services/whatsapp-channel/tsconfig.json && tsc -p services/webchat-channel/tsconfig.json && tsc -p services/telegram-channel/tsconfig.json && NODE_OPTIONS='--max-old-space-size=8192' tsc -b plugins/*/mcp/tsconfig.json",
14
- "build:lib": "tsc -p lib/account-schema-regions/tsconfig.json && tsc -p lib/account-ingest-token/tsconfig.json && tsc -p lib/models/tsconfig.json && tsc -p lib/agent-slug/tsconfig.json && tsc -p lib/mcp-spawn-tee/tsconfig.json && tsc -p lib/mcp-lifeline/tsconfig.json && tsc -p lib/mcp-eager/tsconfig.json && tsc -p lib/account-enumeration/tsconfig.json && tsc -p lib/account-scope/tsconfig.json && tsc -p lib/shared-folder/tsconfig.json && tsc -p lib/graph-write/tsconfig.json && tsc -p lib/ledger-core/tsconfig.json && tsc -p lib/embed-client/tsconfig.json && tsc -p lib/graph-mcp/tsconfig.json && tsc -p lib/graph-trash/tsconfig.json && tsc -p lib/active-rules/tsconfig.json && tsc -p lib/admin-conversation-purge/tsconfig.json && tsc -p lib/graph-search/tsconfig.json && tsc -p lib/graph-style/tsconfig.json && tsc -p lib/device-url/tsconfig.json && tsc -p lib/brand-templating/tsconfig.json && tsc -p lib/entitlement/tsconfig.json && tsc -p lib/task-secrets/tsconfig.json && tsc -p lib/admins-write/tsconfig.json && tsc -p lib/admin-access-password/tsconfig.json && tsc -p lib/persistent-components/tsconfig.json && tsc -p lib/require-port-env/tsconfig.json && tsc -p lib/aeo-llms-txt-writer/tsconfig.json && tsc -p lib/obsidian-parser/tsconfig.json && tsc -p lib/storage-broker/tsconfig.json && tsc -p lib/routine-templates/tsconfig.json && tsc -p lib/agent-dispatch-rule/tsconfig.json && tsc -p lib/shared-folder/tsconfig.json && tsc -p lib/canonical-instant/tsconfig.json && tsc -p lib/dispatch-read/tsconfig.json && tsc -p lib/dispatch-write/tsconfig.json && tsc -p lib/telegram-media/tsconfig.json && tsc -p lib/telegram-managed-bot/tsconfig.json && tsc -p lib/telegram-reach/tsconfig.json",
13
+ "build": "tsc -p lib/account-schema-regions/tsconfig.json && tsc -p lib/account-ingest-token/tsconfig.json && tsc -p lib/models/tsconfig.json && tsc -p lib/agent-slug/tsconfig.json && tsc -p lib/mcp-spawn-tee/tsconfig.json && tsc -p lib/mcp-lifeline/tsconfig.json && tsc -p lib/mcp-eager/tsconfig.json && tsc -p lib/account-enumeration/tsconfig.json && tsc -p lib/account-scope/tsconfig.json && tsc -p lib/shared-folder/tsconfig.json && tsc -p lib/graph-write/tsconfig.json && tsc -p lib/ledger-core/tsconfig.json && tsc -p lib/embed-client/tsconfig.json && tsc -p lib/graph-mcp/tsconfig.json && tsc -p lib/graph-trash/tsconfig.json && tsc -p lib/active-rules/tsconfig.json && tsc -p lib/admin-conversation-purge/tsconfig.json && tsc -p lib/graph-search/tsconfig.json && tsc -p lib/graph-style/tsconfig.json && tsc -p lib/device-url/tsconfig.json && tsc -p lib/brand-templating/tsconfig.json && tsc -p lib/entitlement/tsconfig.json && tsc -p lib/task-secrets/tsconfig.json && tsc -p lib/admins-write/tsconfig.json && tsc -p lib/admin-access-password/tsconfig.json && tsc -p lib/persistent-components/tsconfig.json && tsc -p lib/require-port-env/tsconfig.json && tsc -p lib/aeo-llms-txt-writer/tsconfig.json && tsc -p lib/obsidian-parser/tsconfig.json && tsc -p lib/storage-broker/tsconfig.json && tsc -p lib/routine-templates/tsconfig.json && tsc -p lib/agent-dispatch-rule/tsconfig.json && tsc -p lib/canonical-instant/tsconfig.json && tsc -p lib/dispatch-read/tsconfig.json && tsc -p lib/dispatch-write/tsconfig.json && tsc -p lib/learning-constants/tsconfig.json && tsc -p lib/telegram-button-labels/tsconfig.json && tsc -p lib/telegram-media/tsconfig.json && tsc -p lib/telegram-managed-bot/tsconfig.json && tsc -p lib/telegram-reach/tsconfig.json && tsc -p services/claude-session-manager/tsconfig.json && tsc -p services/whatsapp-channel/tsconfig.json && tsc -p services/webchat-channel/tsconfig.json && tsc -p services/telegram-channel/tsconfig.json && NODE_OPTIONS='--max-old-space-size=8192' tsc -b plugins/*/mcp/tsconfig.json",
14
+ "build:lib": "tsc -p lib/account-schema-regions/tsconfig.json && tsc -p lib/account-ingest-token/tsconfig.json && tsc -p lib/models/tsconfig.json && tsc -p lib/agent-slug/tsconfig.json && tsc -p lib/mcp-spawn-tee/tsconfig.json && tsc -p lib/mcp-lifeline/tsconfig.json && tsc -p lib/mcp-eager/tsconfig.json && tsc -p lib/account-enumeration/tsconfig.json && tsc -p lib/account-scope/tsconfig.json && tsc -p lib/shared-folder/tsconfig.json && tsc -p lib/graph-write/tsconfig.json && tsc -p lib/ledger-core/tsconfig.json && tsc -p lib/embed-client/tsconfig.json && tsc -p lib/graph-mcp/tsconfig.json && tsc -p lib/graph-trash/tsconfig.json && tsc -p lib/active-rules/tsconfig.json && tsc -p lib/admin-conversation-purge/tsconfig.json && tsc -p lib/graph-search/tsconfig.json && tsc -p lib/graph-style/tsconfig.json && tsc -p lib/device-url/tsconfig.json && tsc -p lib/brand-templating/tsconfig.json && tsc -p lib/entitlement/tsconfig.json && tsc -p lib/task-secrets/tsconfig.json && tsc -p lib/admins-write/tsconfig.json && tsc -p lib/admin-access-password/tsconfig.json && tsc -p lib/persistent-components/tsconfig.json && tsc -p lib/require-port-env/tsconfig.json && tsc -p lib/aeo-llms-txt-writer/tsconfig.json && tsc -p lib/obsidian-parser/tsconfig.json && tsc -p lib/storage-broker/tsconfig.json && tsc -p lib/routine-templates/tsconfig.json && tsc -p lib/agent-dispatch-rule/tsconfig.json && tsc -p lib/shared-folder/tsconfig.json && tsc -p lib/canonical-instant/tsconfig.json && tsc -p lib/dispatch-read/tsconfig.json && tsc -p lib/dispatch-write/tsconfig.json && tsc -p lib/telegram-button-labels/tsconfig.json && tsc -p lib/telegram-media/tsconfig.json && tsc -p lib/telegram-managed-bot/tsconfig.json && tsc -p lib/telegram-reach/tsconfig.json",
15
15
  "clean:dist": "node scripts/check-orphan-dist.mjs --remove",
16
16
  "gen:canonical-tools": "node scripts/generate-canonical-tool-names.mjs",
17
17
  "build:memory": "tsc -p plugins/memory/mcp/tsconfig.json",
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: platform-architecture
3
3
  description: Use when grounding any documented-surface claim about what Real Agent ships — plugins, skills, specialists, install/deploy flows, internals. This is the install catalogue, not evidence of what is enabled on the current account. For install state on this account, call `capabilities-here`; for documented surface, cite the `Source:` URL inline.
4
- content-hash: sha256:ea35aa77226e01aad93c7b90e9118e4e993a58cb980330b28541dcbb3122a993
4
+ content-hash: sha256:4d7fafd7d648ca6953247a413d12dd0be25fd9e0c6876f1afb5ece48b1c0acdc
5
5
  brand: realagent-code
6
6
  product-name: Real Agent
7
7
  ---
@@ -3012,6 +3012,13 @@ nobody, and a vote was dropped without a trace. A press on a job-sheet card is t
3012
3012
  one exception: the dispatch plugin applies the change and tells the presser
3013
3013
  itself, so Real Agent does not answer a second time.
3014
3014
 
3015
+ A card carries its own words, so when Real Agent sends a card with buttons
3016
+ it puts the answer in the card itself and does not add a sentence beside it. On a
3017
+ public bot, that following sentence is held back on purpose, so the visitor sees
3018
+ the card once and not the same thing twice. And when someone presses a button,
3019
+ your conversation view shows the button's own wording — "1 passenger", "One way"
3020
+ — rather than the code behind it.
3021
+
3015
3022
  The same is now true of most of what Telegram sends. Reply to one of
3016
3023
  Real Agent's messages and it sees the message you replied to, so "yes, do
3017
3024
  that one" means something. React to a message with an emoji and it hears the
@@ -9,6 +9,11 @@ Invoked by the admin agent directly.
9
9
 
10
10
  This is the platform's release timeline, newest first. Each entry shows the date it shipped and the version it shipped in, so you can tell the operator how current their install is. To compare, read the installed version from `capabilities-here` and match it against the versions below. Keep answers high level and in plain English; this is a summary, not a full commit log.
11
11
 
12
+ ## 2026-08-11 (0.1.595)
13
+
14
+ - A Telegram bot no longer describes the card it just sent you. The card arrived and the bot then narrated it in a second message, because the rule that withholds that text never took effect.
15
+ - Pressing a button now reads as the button's own label rather than the code behind it. A standing check reports if card text stops being withheld again.
16
+
12
17
  ## 2026-08-11 (0.1.594)
13
18
 
14
19
  - A public Telegram bot no longer tells people it cannot do things it can. Its own description denied the tools it had actually been given, so it refused work it was equipped for.
@@ -189,6 +189,13 @@ nobody, and a vote was dropped without a trace. A press on a job-sheet card is t
189
189
  one exception: the dispatch plugin applies the change and tells the presser
190
190
  itself, so {{productName}} does not answer a second time.
191
191
 
192
+ A card carries its own words, so when {{productName}} sends a card with buttons
193
+ it puts the answer in the card itself and does not add a sentence beside it. On a
194
+ public bot, that following sentence is held back on purpose, so the visitor sees
195
+ the card once and not the same thing twice. And when someone presses a button,
196
+ your conversation view shows the button's own wording — "1 passenger", "One way"
197
+ — rather than the code behind it.
198
+
192
199
  The same is now true of most of what Telegram sends. Reply to one of
193
200
  {{productName}}'s messages and it sees the message you replied to, so "yes, do
194
201
  that one" means something. React to a message with an emoji and it hears the