@llblab/pi-telegram 0.46.0 → 0.48.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/AGENTS.md +4 -2
- package/BACKLOG.md +0 -6
- package/CHANGELOG.md +14 -0
- package/README.md +5 -2
- package/dist/api/activity.d.ts +6 -0
- package/dist/api/activity.js +6 -0
- package/dist/api/commands.d.ts +6 -0
- package/dist/api/commands.js +6 -0
- package/dist/api/delivery.d.ts +6 -0
- package/dist/api/delivery.js +6 -0
- package/dist/api/inbound.d.ts +6 -0
- package/dist/api/inbound.js +6 -0
- package/dist/api/keyboard.d.ts +6 -0
- package/dist/api/keyboard.js +6 -0
- package/dist/api/outbound.d.ts +6 -0
- package/dist/api/outbound.js +6 -0
- package/dist/api/sections.d.ts +7 -0
- package/dist/api/sections.js +6 -0
- package/dist/api/status.d.ts +6 -0
- package/dist/api/status.js +6 -0
- package/dist/api/updates.d.ts +6 -0
- package/dist/api/updates.js +6 -0
- package/dist/api/voice.d.ts +6 -0
- package/dist/api/voice.js +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -0
- package/dist/lib/activity-verbosity.d.ts +52 -0
- package/dist/lib/activity-verbosity.js +596 -0
- package/dist/lib/activity.d.ts +220 -0
- package/dist/lib/activity.js +574 -0
- package/dist/lib/agent-messages.d.ts +28 -0
- package/dist/lib/agent-messages.js +86 -0
- package/dist/lib/bindings.d.ts +250 -0
- package/dist/lib/bindings.js +936 -0
- package/dist/lib/bus-api.d.ts +18 -0
- package/dist/lib/bus-api.js +254 -0
- package/dist/lib/bus-follower.d.ts +464 -0
- package/dist/lib/bus-follower.js +1686 -0
- package/dist/lib/bus-leader.d.ts +292 -0
- package/dist/lib/bus-leader.js +2242 -0
- package/dist/lib/bus-transport.d.ts +64 -0
- package/dist/lib/bus-transport.js +140 -0
- package/dist/lib/bus.d.ts +518 -0
- package/dist/lib/bus.js +2055 -0
- package/dist/lib/channel-posts.d.ts +170 -0
- package/dist/lib/channel-posts.js +611 -0
- package/dist/lib/command-templates.d.ts +70 -0
- package/dist/lib/command-templates.js +744 -0
- package/dist/lib/commands.d.ts +541 -0
- package/dist/lib/commands.js +1155 -0
- package/dist/lib/config.d.ts +252 -0
- package/dist/lib/config.js +889 -0
- package/dist/lib/delivery.d.ts +163 -0
- package/dist/lib/delivery.js +542 -0
- package/dist/lib/extension.d.ts +7 -0
- package/dist/lib/extension.js +1604 -0
- package/dist/lib/generative-app-worker.mjs +104 -0
- package/dist/lib/generative-apps.d.ts +212 -0
- package/dist/lib/generative-apps.js +1006 -0
- package/dist/lib/inbound.d.ts +91 -0
- package/dist/lib/inbound.js +502 -0
- package/dist/lib/journal.d.ts +665 -0
- package/dist/lib/journal.js +3701 -0
- package/dist/lib/keyboard.d.ts +23 -0
- package/dist/lib/keyboard.js +37 -0
- package/dist/lib/lifecycle.d.ts +157 -0
- package/dist/lib/lifecycle.js +395 -0
- package/dist/lib/locks.d.ts +164 -0
- package/dist/lib/locks.js +1208 -0
- package/dist/lib/logging.d.ts +50 -0
- package/dist/lib/logging.js +274 -0
- package/dist/lib/media.d.ts +181 -0
- package/dist/lib/media.js +602 -0
- package/dist/lib/menu-model.d.ts +217 -0
- package/dist/lib/menu-model.js +663 -0
- package/dist/lib/menu-queue.d.ts +37 -0
- package/dist/lib/menu-queue.js +408 -0
- package/dist/lib/menu-settings.d.ts +115 -0
- package/dist/lib/menu-settings.js +595 -0
- package/dist/lib/menu-status.d.ts +32 -0
- package/dist/lib/menu-status.js +112 -0
- package/dist/lib/menu-thinking.d.ts +29 -0
- package/dist/lib/menu-thinking.js +82 -0
- package/dist/lib/menu.d.ts +171 -0
- package/dist/lib/menu.js +323 -0
- package/dist/lib/model.d.ts +127 -0
- package/dist/lib/model.js +409 -0
- package/dist/lib/outbound-attachments.d.ts +241 -0
- package/dist/lib/outbound-attachments.js +639 -0
- package/dist/lib/outbound-buttons.d.ts +69 -0
- package/dist/lib/outbound-buttons.js +248 -0
- package/dist/lib/outbound-markup.d.ts +42 -0
- package/dist/lib/outbound-markup.js +678 -0
- package/dist/lib/outbound-voice.d.ts +55 -0
- package/dist/lib/outbound-voice.js +152 -0
- package/dist/lib/outbound.d.ts +185 -0
- package/dist/lib/outbound.js +516 -0
- package/dist/lib/ownership.d.ts +77 -0
- package/dist/lib/ownership.js +174 -0
- package/dist/lib/paths.d.ts +40 -0
- package/dist/lib/paths.js +94 -0
- package/dist/lib/pi.d.ts +72 -0
- package/dist/lib/pi.js +121 -0
- package/dist/lib/polling.d.ts +351 -0
- package/dist/lib/polling.js +1196 -0
- package/dist/lib/preview.d.ts +192 -0
- package/dist/lib/preview.js +614 -0
- package/dist/lib/prompt-templates.d.ts +24 -0
- package/dist/lib/prompt-templates.js +118 -0
- package/dist/lib/prompts.d.ts +57 -0
- package/dist/lib/prompts.js +167 -0
- package/dist/lib/queue.d.ts +766 -0
- package/dist/lib/queue.js +1965 -0
- package/dist/lib/recovery.d.ts +86 -0
- package/dist/lib/recovery.js +285 -0
- package/dist/lib/rendering.d.ts +20 -0
- package/dist/lib/rendering.js +983 -0
- package/dist/lib/replies.d.ts +214 -0
- package/dist/lib/replies.js +737 -0
- package/dist/lib/routing.d.ts +207 -0
- package/dist/lib/routing.js +2282 -0
- package/dist/lib/runtime.d.ts +172 -0
- package/dist/lib/runtime.js +402 -0
- package/dist/lib/sections.d.ts +165 -0
- package/dist/lib/sections.js +327 -0
- package/dist/lib/setup.d.ts +82 -0
- package/dist/lib/setup.js +150 -0
- package/dist/lib/skills.d.ts +8 -0
- package/dist/lib/skills.js +12 -0
- package/dist/lib/status.d.ts +442 -0
- package/dist/lib/status.js +1008 -0
- package/dist/lib/sync.d.ts +179 -0
- package/dist/lib/sync.js +782 -0
- package/dist/lib/target.d.ts +20 -0
- package/dist/lib/target.js +27 -0
- package/dist/lib/telegram-api.d.ts +541 -0
- package/dist/lib/telegram-api.js +1159 -0
- package/dist/lib/text-groups.d.ts +89 -0
- package/dist/lib/text-groups.js +317 -0
- package/dist/lib/thread-cleanup-manager.d.ts +288 -0
- package/dist/lib/thread-cleanup-manager.js +560 -0
- package/dist/lib/thread-display.d.ts +46 -0
- package/dist/lib/thread-display.js +257 -0
- package/dist/lib/thread-naming.d.ts +46 -0
- package/dist/lib/thread-naming.js +78 -0
- package/dist/lib/thread-reconciler.d.ts +239 -0
- package/dist/lib/thread-reconciler.js +644 -0
- package/dist/lib/threads.d.ts +621 -0
- package/dist/lib/threads.js +3679 -0
- package/dist/lib/time-injection.d.ts +15 -0
- package/dist/lib/time-injection.js +56 -0
- package/dist/lib/turns.d.ts +109 -0
- package/dist/lib/turns.js +500 -0
- package/dist/lib/updates.d.ts +1290 -0
- package/dist/lib/updates.js +3634 -0
- package/dist/lib/voice.d.ts +117 -0
- package/dist/lib/voice.js +174 -0
- package/dist/lib/workspace-admission.d.ts +264 -0
- package/dist/lib/workspace-admission.js +1136 -0
- package/dist/lib/workspace-retirement.d.ts +219 -0
- package/dist/lib/workspace-retirement.js +587 -0
- package/dist/lib/workspace-slots.d.ts +30 -0
- package/dist/lib/workspace-slots.js +54 -0
- package/dist/package.json +126 -0
- package/dist/pi-telegram/index.js +1 -0
- package/dist/skills/generated-control-surface/SKILL.md +107 -0
- package/dist/skills/generated-control-surface/references/capability-adapters.md +27 -0
- package/dist/skills/generated-control-surface/references/layout-and-state.md +37 -0
- package/dist/skills/generative-apps/SKILL.md +115 -0
- package/dist/skills/show-me/SKILL.md +166 -0
- package/dist/skills/show-me/references/telegram-surfaces.md +43 -0
- package/dist/skills/telegram-bridge/SKILL.md +129 -0
- package/dist/skills/telegram-bridge/references/configuration.md +15 -0
- package/dist/skills/telegram-bridge/references/delivery-and-threads.md +27 -0
- package/dist/skills/telegram-bridge/references/diagnosis.md +18 -0
- package/docs/README.md +2 -0
- package/docs/architecture.md +1 -1
- package/docs/compact-matrix-literal.md +10 -3
- package/docs/generative-apps.md +15 -13
- package/docs/multi-instance-bus.md +6 -4
- package/docs/outbound.md +1 -1
- package/docs/public-api.md +3 -3
- package/lib/bindings.ts +89 -6
- package/lib/bus-follower.ts +7 -3
- package/lib/bus-leader.ts +44 -19
- package/lib/bus.ts +4 -1
- package/lib/commands.ts +2 -2
- package/lib/config.ts +14 -7
- package/lib/delivery.ts +38 -6
- package/lib/extension.ts +7 -0
- package/lib/generative-apps.ts +379 -13
- package/lib/menu-settings.ts +20 -6
- package/lib/outbound-markup.ts +16 -7
- package/lib/status.ts +15 -12
- package/lib/telegram-api.ts +12 -1
- package/lib/thread-display.ts +96 -18
- package/package.json +56 -13
- package/scripts/build-dist.mjs +44 -0
- package/scripts/measure-bus.mjs +8 -1
- package/scripts/measure-workspace.mjs +8 -1
- package/skills/generative-apps/SKILL.md +1 -1
- package/skills/show-me/SKILL.md +1 -1
- package/skills/show-me/references/telegram-surfaces.md +1 -1
- package/skills/telegram-bridge/SKILL.md +1 -1
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telegram message ownership helpers
|
|
3
|
+
* Zones: telegram routing, multi-instance bus, in-memory coordination
|
|
4
|
+
* Owns message-id ownership and live forwarding projections so inbound updates resolve back to the instance/target that owns them
|
|
5
|
+
*/
|
|
6
|
+
import { TELEGRAM_BUS_CAPABILITY_DURABLE_FOLLOWER_ADMISSION, } from "./bus.js";
|
|
7
|
+
import { getTelegramTargetKey } from "./target.js";
|
|
8
|
+
function getTelegramFollowerOwnershipGeneration(follower) {
|
|
9
|
+
return (follower.registrationGeneration ??
|
|
10
|
+
`${follower.instanceId}:${follower.connectedAtMs}`);
|
|
11
|
+
}
|
|
12
|
+
export function createTelegramBusMessageOwnershipRuntime(deps) {
|
|
13
|
+
const store = createTelegramMessageOwnershipStore({
|
|
14
|
+
getProfileKey: deps.getProfileKey,
|
|
15
|
+
isOwnerGenerationLive(record) {
|
|
16
|
+
if (!record.ownerGeneration)
|
|
17
|
+
return true;
|
|
18
|
+
return deps.listFollowers().some((follower) => {
|
|
19
|
+
return (follower.instanceId === record.instanceId &&
|
|
20
|
+
getTelegramFollowerOwnershipGeneration(follower) ===
|
|
21
|
+
record.ownerGeneration);
|
|
22
|
+
});
|
|
23
|
+
},
|
|
24
|
+
resolveOwnerReplacement(record) {
|
|
25
|
+
if (!record.recipientBindingKey)
|
|
26
|
+
return undefined;
|
|
27
|
+
const follower = deps
|
|
28
|
+
.listFollowers()
|
|
29
|
+
.find((candidate) => candidate.profileKey === record.recipientBindingKey);
|
|
30
|
+
return follower
|
|
31
|
+
? {
|
|
32
|
+
instanceId: follower.instanceId,
|
|
33
|
+
ownerGeneration: getTelegramFollowerOwnershipGeneration(follower),
|
|
34
|
+
recipientBindingKey: record.recipientBindingKey,
|
|
35
|
+
}
|
|
36
|
+
: undefined;
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
const recordFollower = function (input) {
|
|
40
|
+
return store.record({
|
|
41
|
+
chatId: input.chatId,
|
|
42
|
+
messageId: input.messageId,
|
|
43
|
+
target: input.target,
|
|
44
|
+
instanceId: input.follower.instanceId,
|
|
45
|
+
ownerGeneration: getTelegramFollowerOwnershipGeneration(input.follower),
|
|
46
|
+
recipientBindingKey: input.follower.profileKey,
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
return {
|
|
50
|
+
store,
|
|
51
|
+
getForwardOwnership(chatId, messageId) {
|
|
52
|
+
const record = store.get(chatId, messageId);
|
|
53
|
+
if (!record?.ownerGeneration || !record.recipientBindingKey)
|
|
54
|
+
return record;
|
|
55
|
+
const follower = deps.listFollowers().find((candidate) => candidate.instanceId === record.instanceId &&
|
|
56
|
+
candidate.registrationGeneration === record.ownerGeneration &&
|
|
57
|
+
candidate.profileKey === record.recipientBindingKey);
|
|
58
|
+
// Protocol is live registration authority, not message-cache history.
|
|
59
|
+
// Keep an incomplete known owner foreign so the forwarder fails closed.
|
|
60
|
+
return follower?.protocol?.capabilities.includes(TELEGRAM_BUS_CAPABILITY_DURABLE_FOLLOWER_ADMISSION) ? { ...record, protocolIdentity: follower.protocol } : record;
|
|
61
|
+
},
|
|
62
|
+
recordLocal(input) {
|
|
63
|
+
return store.record({ ...input, instanceId: deps.instanceId });
|
|
64
|
+
},
|
|
65
|
+
recordRouted(input) {
|
|
66
|
+
const follower = deps
|
|
67
|
+
.listFollowers()
|
|
68
|
+
.find((candidate) => candidate.instanceId === input.instanceId);
|
|
69
|
+
return follower
|
|
70
|
+
? recordFollower({ ...input, follower })
|
|
71
|
+
: store.record(input);
|
|
72
|
+
},
|
|
73
|
+
recordFollower,
|
|
74
|
+
isOwnedByFollower({ chatId, messageId, follower }) {
|
|
75
|
+
const ownership = store.get(chatId, messageId);
|
|
76
|
+
return (ownership?.instanceId === follower.instanceId &&
|
|
77
|
+
ownership.ownerGeneration ===
|
|
78
|
+
getTelegramFollowerOwnershipGeneration(follower));
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function getTelegramMessageOwnershipKey(chatId, messageId, profileKey) {
|
|
83
|
+
return `${profileKey ?? ""}:${chatId}:${messageId}`;
|
|
84
|
+
}
|
|
85
|
+
function createTelegramMessageOwnershipRecord(input) {
|
|
86
|
+
return {
|
|
87
|
+
chatId: input.chatId,
|
|
88
|
+
messageId: input.messageId,
|
|
89
|
+
target: input.target ?? { chatId: input.chatId },
|
|
90
|
+
instanceId: input.instanceId,
|
|
91
|
+
...(input.profileKey ? { profileKey: input.profileKey } : {}),
|
|
92
|
+
...(input.ownerGeneration
|
|
93
|
+
? { ownerGeneration: input.ownerGeneration }
|
|
94
|
+
: {}),
|
|
95
|
+
...(input.recipientBindingKey
|
|
96
|
+
? { recipientBindingKey: input.recipientBindingKey }
|
|
97
|
+
: {}),
|
|
98
|
+
createdAt: input.previous?.createdAt ?? input.now,
|
|
99
|
+
updatedAt: input.now,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
export function createTelegramMessageOwnershipStore(options = {}) {
|
|
103
|
+
const records = new Map();
|
|
104
|
+
return {
|
|
105
|
+
record: (input) => {
|
|
106
|
+
const profileKey = input.profileKey ?? options.getProfileKey?.();
|
|
107
|
+
const key = getTelegramMessageOwnershipKey(input.chatId, input.messageId, profileKey);
|
|
108
|
+
const now = input.now ?? Date.now();
|
|
109
|
+
const record = createTelegramMessageOwnershipRecord({
|
|
110
|
+
...input,
|
|
111
|
+
profileKey,
|
|
112
|
+
now,
|
|
113
|
+
previous: records.get(key),
|
|
114
|
+
});
|
|
115
|
+
records.set(key, record);
|
|
116
|
+
return record;
|
|
117
|
+
},
|
|
118
|
+
get: (chatId, messageId) => {
|
|
119
|
+
const key = getTelegramMessageOwnershipKey(chatId, messageId, options.getProfileKey?.());
|
|
120
|
+
const record = records.get(key);
|
|
121
|
+
if (!record)
|
|
122
|
+
return undefined;
|
|
123
|
+
if (record.ownerGeneration &&
|
|
124
|
+
options.isOwnerGenerationLive &&
|
|
125
|
+
!options.isOwnerGenerationLive(record)) {
|
|
126
|
+
const replacement = options.resolveOwnerReplacement?.(record);
|
|
127
|
+
if (!replacement)
|
|
128
|
+
return undefined;
|
|
129
|
+
const rebound = { ...record, ...replacement };
|
|
130
|
+
records.set(key, rebound);
|
|
131
|
+
return rebound;
|
|
132
|
+
}
|
|
133
|
+
return record;
|
|
134
|
+
},
|
|
135
|
+
forget: (chatId, messageId) => records.delete(getTelegramMessageOwnershipKey(chatId, messageId, options.getProfileKey?.())),
|
|
136
|
+
forgetTarget: (target) => {
|
|
137
|
+
const targetKey = getTelegramTargetKey(target);
|
|
138
|
+
const profileKey = options.getProfileKey?.();
|
|
139
|
+
let removed = 0;
|
|
140
|
+
for (const [key, record] of records) {
|
|
141
|
+
if ((record.profileKey ?? undefined) !== profileKey)
|
|
142
|
+
continue;
|
|
143
|
+
if (getTelegramTargetKey(record.target) !== targetKey)
|
|
144
|
+
continue;
|
|
145
|
+
records.delete(key);
|
|
146
|
+
removed += 1;
|
|
147
|
+
}
|
|
148
|
+
return removed;
|
|
149
|
+
},
|
|
150
|
+
prune: ({ now, maxAgeMs, maxRecords }) => {
|
|
151
|
+
let removed = 0;
|
|
152
|
+
if (maxAgeMs !== undefined) {
|
|
153
|
+
for (const [key, record] of records) {
|
|
154
|
+
if (now - record.updatedAt <= maxAgeMs)
|
|
155
|
+
continue;
|
|
156
|
+
records.delete(key);
|
|
157
|
+
removed += 1;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (maxRecords !== undefined && records.size > maxRecords) {
|
|
161
|
+
const oldest = [...records.entries()].sort((left, right) => left[1].updatedAt - right[1].updatedAt);
|
|
162
|
+
for (const [key] of oldest.slice(0, records.size - maxRecords)) {
|
|
163
|
+
records.delete(key);
|
|
164
|
+
removed += 1;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return removed;
|
|
168
|
+
},
|
|
169
|
+
entries: () => [...records.values()],
|
|
170
|
+
clear: () => {
|
|
171
|
+
records.clear();
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export declare const TELEGRAM_DEFAULT_PROFILE_NAME = "default";
|
|
2
|
+
export interface TelegramAgentDirResolutionInput {
|
|
3
|
+
env?: Partial<Pick<NodeJS.ProcessEnv, "PI_CODING_AGENT_DIR">>;
|
|
4
|
+
execPath?: string;
|
|
5
|
+
argv?: readonly string[];
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Resolve the agent data directory for the current Pi-compatible runtime.
|
|
9
|
+
*
|
|
10
|
+
* Precedence:
|
|
11
|
+
* 1. `PI_CODING_AGENT_DIR` env variable, when explicitly set.
|
|
12
|
+
* 2. Detect Pi-compatible runtime identity from the executable or argv[1]
|
|
13
|
+
* (e.g. OMP vs standard Pi agent).
|
|
14
|
+
* 3. Fallback: `~/.pi/agent`.
|
|
15
|
+
*/
|
|
16
|
+
export declare function resolveAgentDir(input?: TelegramAgentDirResolutionInput): string;
|
|
17
|
+
/** Telegram bridge configuration file (<agentDir>/telegram.json). */
|
|
18
|
+
export declare function resolveTelegramConfigPath(): string;
|
|
19
|
+
/** Telegram bridge temporary directory (<agentDir>/tmp/telegram). */
|
|
20
|
+
export declare function resolveTelegramTempDir(agentDir?: string): string;
|
|
21
|
+
/** Telegram transport ownership store (<agentDir>/tmp/telegram/owners.json). */
|
|
22
|
+
export declare function resolveTelegramOwnersPath(): string;
|
|
23
|
+
export declare function getTelegramProfilePathSuffix(profileName?: string): string;
|
|
24
|
+
export declare function resolveTelegramProfileTempFilePath(baseName: string, extension: string, agentDir?: string, profileName?: string): string;
|
|
25
|
+
export declare function getTelegramDiagnosticsDisplayPaths(profileName?: string): {
|
|
26
|
+
state: string;
|
|
27
|
+
logs: string;
|
|
28
|
+
};
|
|
29
|
+
/** Durable Workspace admission ledger (<agentDir>/tmp/telegram/workspace-admission[.<profile>].json). */
|
|
30
|
+
export declare function resolveTelegramWorkspaceAdmissionPath(agentDir?: string, profileName?: string): string;
|
|
31
|
+
/** Durable inactive Thread cleanup work-set journal. */
|
|
32
|
+
export declare function resolveTelegramThreadCleanupWorkPath(agentDir?: string, profileName?: string): string;
|
|
33
|
+
/** Durable agent-authored channel post journal. */
|
|
34
|
+
export declare function resolveTelegramChannelPostJournalPath(agentDir?: string, profileName?: string): string;
|
|
35
|
+
/** Durable inbound update journal (<agentDir>/tmp/telegram/inbox[.<profile>].json). */
|
|
36
|
+
export declare function resolveTelegramUpdateJournalPath(agentDir?: string, profileName?: string): string;
|
|
37
|
+
/** Durable follower delivery journal, isolated by stable recipient binding. */
|
|
38
|
+
export declare function resolveTelegramFollowerJournalPath(recipientBindingKey: string, agentDir?: string, profileName?: string): string;
|
|
39
|
+
/** Runtime event log (<agentDir>/tmp/telegram/logs.jsonl). */
|
|
40
|
+
export declare function resolveTelegramRuntimeLogPath(): string;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telegram bridge path resolution for Pi-compatible runtimes
|
|
3
|
+
* Zones: telemetry paths, filesystem, runtime identity
|
|
4
|
+
* Owns agent-dir detection and extension-local path derivation
|
|
5
|
+
*
|
|
6
|
+
* This domain is pure/path-only: it resolves directories and file paths
|
|
7
|
+
* from environment and runtime identity. It does not read config, manage
|
|
8
|
+
* state, or import broader Telegram domains.
|
|
9
|
+
*/
|
|
10
|
+
import { createHash } from "node:crypto";
|
|
11
|
+
import { homedir } from "node:os";
|
|
12
|
+
import { join, resolve } from "node:path";
|
|
13
|
+
export const TELEGRAM_DEFAULT_PROFILE_NAME = "default";
|
|
14
|
+
/**
|
|
15
|
+
* Resolve the agent data directory for the current Pi-compatible runtime.
|
|
16
|
+
*
|
|
17
|
+
* Precedence:
|
|
18
|
+
* 1. `PI_CODING_AGENT_DIR` env variable, when explicitly set.
|
|
19
|
+
* 2. Detect Pi-compatible runtime identity from the executable or argv[1]
|
|
20
|
+
* (e.g. OMP vs standard Pi agent).
|
|
21
|
+
* 3. Fallback: `~/.pi/agent`.
|
|
22
|
+
*/
|
|
23
|
+
export function resolveAgentDir(input = {}) {
|
|
24
|
+
const env = input.env ?? process.env;
|
|
25
|
+
if (env.PI_CODING_AGENT_DIR)
|
|
26
|
+
return resolve(env.PI_CODING_AGENT_DIR);
|
|
27
|
+
const execPath = input.execPath ?? process.execPath;
|
|
28
|
+
const argv = input.argv ?? process.argv;
|
|
29
|
+
const execBasename = execPath.toLowerCase().split(/[\\/]/u).pop() ?? "";
|
|
30
|
+
const argv1Last = (argv[1] ?? "").toLowerCase().split(/[\\/]/u).pop() ?? "";
|
|
31
|
+
if (execBasename.startsWith("omp") || argv1Last.startsWith("omp")) {
|
|
32
|
+
return join(homedir(), ".omp", "agent");
|
|
33
|
+
}
|
|
34
|
+
return join(homedir(), ".pi", "agent");
|
|
35
|
+
}
|
|
36
|
+
/** Telegram bridge configuration file (<agentDir>/telegram.json). */
|
|
37
|
+
export function resolveTelegramConfigPath() {
|
|
38
|
+
return join(resolveAgentDir(), "telegram.json");
|
|
39
|
+
}
|
|
40
|
+
/** Telegram bridge temporary directory (<agentDir>/tmp/telegram). */
|
|
41
|
+
export function resolveTelegramTempDir(agentDir = resolveAgentDir()) {
|
|
42
|
+
return join(agentDir, "tmp", "telegram");
|
|
43
|
+
}
|
|
44
|
+
/** Telegram transport ownership store (<agentDir>/tmp/telegram/owners.json). */
|
|
45
|
+
export function resolveTelegramOwnersPath() {
|
|
46
|
+
return join(resolveTelegramTempDir(), "owners.json");
|
|
47
|
+
}
|
|
48
|
+
export function getTelegramProfilePathSuffix(profileName) {
|
|
49
|
+
if (!profileName || profileName === TELEGRAM_DEFAULT_PROFILE_NAME)
|
|
50
|
+
return "";
|
|
51
|
+
return `.${profileName.replace(/[^a-zA-Z0-9._-]+/g, "_")}`;
|
|
52
|
+
}
|
|
53
|
+
export function resolveTelegramProfileTempFilePath(baseName, extension, agentDir = resolveAgentDir(), profileName) {
|
|
54
|
+
return join(resolveTelegramTempDir(agentDir), `${baseName}${getTelegramProfilePathSuffix(profileName)}.${extension}`);
|
|
55
|
+
}
|
|
56
|
+
export function getTelegramDiagnosticsDisplayPaths(profileName) {
|
|
57
|
+
const suffix = getTelegramProfilePathSuffix(profileName);
|
|
58
|
+
const profileSlug = suffix.slice(1);
|
|
59
|
+
return {
|
|
60
|
+
state: `~/.pi/agent/tmp/telegram/state${suffix}.json`,
|
|
61
|
+
logs: `~/.pi/agent/tmp/telegram/logs${profileSlug ? `.${profileSlug}` : ""}.jsonl`,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/** Durable Workspace admission ledger (<agentDir>/tmp/telegram/workspace-admission[.<profile>].json). */
|
|
65
|
+
export function resolveTelegramWorkspaceAdmissionPath(agentDir = resolveAgentDir(), profileName) {
|
|
66
|
+
return resolveTelegramProfileTempFilePath("workspace-admission", "json", agentDir, profileName);
|
|
67
|
+
}
|
|
68
|
+
/** Durable inactive Thread cleanup work-set journal. */
|
|
69
|
+
export function resolveTelegramThreadCleanupWorkPath(agentDir = resolveAgentDir(), profileName) {
|
|
70
|
+
return resolveTelegramProfileTempFilePath("thread-cleanup", "json", agentDir, profileName);
|
|
71
|
+
}
|
|
72
|
+
/** Durable agent-authored channel post journal. */
|
|
73
|
+
export function resolveTelegramChannelPostJournalPath(agentDir = resolveAgentDir(), profileName) {
|
|
74
|
+
return resolveTelegramProfileTempFilePath("channel-posts", "json", agentDir, profileName);
|
|
75
|
+
}
|
|
76
|
+
/** Durable inbound update journal (<agentDir>/tmp/telegram/inbox[.<profile>].json). */
|
|
77
|
+
export function resolveTelegramUpdateJournalPath(agentDir = resolveAgentDir(), profileName) {
|
|
78
|
+
return resolveTelegramProfileTempFilePath("inbox", "json", agentDir, profileName);
|
|
79
|
+
}
|
|
80
|
+
/** Durable follower delivery journal, isolated by stable recipient binding. */
|
|
81
|
+
export function resolveTelegramFollowerJournalPath(recipientBindingKey, agentDir = resolveAgentDir(), profileName) {
|
|
82
|
+
if (!recipientBindingKey) {
|
|
83
|
+
throw new Error("Telegram follower journal binding key is required.");
|
|
84
|
+
}
|
|
85
|
+
const bindingHash = createHash("sha256")
|
|
86
|
+
.update(recipientBindingKey)
|
|
87
|
+
.digest("hex")
|
|
88
|
+
.slice(0, 16);
|
|
89
|
+
return resolveTelegramProfileTempFilePath(`follower-inbox-${bindingHash}`, "json", agentDir, profileName);
|
|
90
|
+
}
|
|
91
|
+
/** Runtime event log (<agentDir>/tmp/telegram/logs.jsonl). */
|
|
92
|
+
export function resolveTelegramRuntimeLogPath() {
|
|
93
|
+
return resolveTelegramProfileTempFilePath("logs", "jsonl");
|
|
94
|
+
}
|
package/dist/lib/pi.d.ts
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pi SDK adapter boundary
|
|
3
|
+
* Zones: pi agent sdk boundary, shared adapters
|
|
4
|
+
* Owns direct pi SDK imports and exposes narrow bridge-facing helpers/types for the extension composition layer
|
|
5
|
+
*/
|
|
6
|
+
import type { AssistantMessageEvent } from "@earendil-works/pi-ai";
|
|
7
|
+
import { type AgentEndEvent, type AgentSettledEvent, type AgentStartEvent, type BeforeAgentStartEvent, type ExtensionAPI, type ExtensionCommandContext, type ExtensionContext, type InputEvent, type MessageEndEvent, type SessionBeforeCompactEvent, type SessionCompactEvent, type SessionShutdownEvent, type SessionStartEvent, type SlashCommandInfo, type UIPromptEndEvent, type UIPromptStartEvent } from "@earendil-works/pi-coding-agent";
|
|
8
|
+
export type { AgentEndEvent, AgentSettledEvent, AgentStartEvent, AssistantMessageEvent, BeforeAgentStartEvent, ExtensionAPI, ExtensionCommandContext, ExtensionContext, InputEvent, MessageEndEvent, SessionBeforeCompactEvent, SessionCompactEvent, SessionShutdownEvent, SessionStartEvent, SlashCommandInfo, UIPromptEndEvent, UIPromptStartEvent, };
|
|
9
|
+
export interface SessionCompactFailedEvent {
|
|
10
|
+
type: "session_compact_failed";
|
|
11
|
+
reason: "manual" | "threshold" | "overflow";
|
|
12
|
+
errorMessage?: string;
|
|
13
|
+
aborted: boolean;
|
|
14
|
+
willRetry: boolean;
|
|
15
|
+
fromExtension: boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface ToolExecutionStartEvent {
|
|
18
|
+
type: "tool_execution_start";
|
|
19
|
+
toolCallId: string;
|
|
20
|
+
toolName: string;
|
|
21
|
+
args: unknown;
|
|
22
|
+
}
|
|
23
|
+
export interface ToolExecutionUpdateEvent {
|
|
24
|
+
type: "tool_execution_update";
|
|
25
|
+
toolCallId: string;
|
|
26
|
+
toolName: string;
|
|
27
|
+
args: unknown;
|
|
28
|
+
partialResult: unknown;
|
|
29
|
+
}
|
|
30
|
+
export interface ToolExecutionEndEvent {
|
|
31
|
+
type: "tool_execution_end";
|
|
32
|
+
toolCallId: string;
|
|
33
|
+
toolName: string;
|
|
34
|
+
result: unknown;
|
|
35
|
+
isError: boolean;
|
|
36
|
+
}
|
|
37
|
+
export interface PiSettingsManager {
|
|
38
|
+
reload: () => Promise<void>;
|
|
39
|
+
flush: () => Promise<void>;
|
|
40
|
+
getEnabledModels: () => string[] | undefined;
|
|
41
|
+
setEnabledModels: (patterns: string[] | undefined) => void;
|
|
42
|
+
}
|
|
43
|
+
export type PiSlashCommandInfo = SlashCommandInfo;
|
|
44
|
+
export type PiRunMode = "tui" | "rpc" | "json" | "print";
|
|
45
|
+
export declare function getExtensionContextMode(ctx: unknown): PiRunMode | undefined;
|
|
46
|
+
export declare function isExtensionContextPassiveRunMode(ctx: unknown): boolean;
|
|
47
|
+
export declare function canStartPollingInExtensionContext(ctx: unknown): boolean;
|
|
48
|
+
export declare function formatPollingStartBlockedByRunMode(ctx: unknown): string;
|
|
49
|
+
export declare function getSessionCompactionReason(event: unknown): "manual" | "threshold" | "overflow" | "unknown";
|
|
50
|
+
export interface PiExtensionApiRuntimePorts {
|
|
51
|
+
sendUserMessage: ExtensionAPI["sendUserMessage"];
|
|
52
|
+
exec: ExtensionAPI["exec"];
|
|
53
|
+
getCommands: ExtensionAPI["getCommands"];
|
|
54
|
+
getThinkingLevel: ExtensionAPI["getThinkingLevel"];
|
|
55
|
+
setThinkingLevel: ExtensionAPI["setThinkingLevel"];
|
|
56
|
+
getActiveTools: ExtensionAPI["getActiveTools"];
|
|
57
|
+
setActiveTools: ExtensionAPI["setActiveTools"];
|
|
58
|
+
setModel: ExtensionAPI["setModel"];
|
|
59
|
+
}
|
|
60
|
+
export declare function createExtensionApiRuntimePorts(api: Pick<ExtensionAPI, "sendUserMessage" | "exec" | "getCommands" | "getThinkingLevel" | "setThinkingLevel" | "getActiveTools" | "setActiveTools" | "setModel">): PiExtensionApiRuntimePorts;
|
|
61
|
+
export declare function normalizeSettingsManager(manager: unknown): PiSettingsManager;
|
|
62
|
+
export declare function createSettingsManager(cwd: string): Promise<PiSettingsManager>;
|
|
63
|
+
export declare function createScopedModelPatternPersister(deps: {
|
|
64
|
+
createSettingsManager: (cwd: string) => PiSettingsManager | PromiseLike<PiSettingsManager>;
|
|
65
|
+
clearCachedModelMenuInputs: () => void;
|
|
66
|
+
}): (patterns: string[], ctx: ExtensionContext) => Promise<void>;
|
|
67
|
+
export declare function getExtensionContextModel(ctx: ExtensionContext): ExtensionContext["model"];
|
|
68
|
+
export declare function getExtensionContextCwd(ctx: ExtensionContext): string;
|
|
69
|
+
export declare function getExtensionContextSessionId(ctx: ExtensionContext): string;
|
|
70
|
+
export declare function isExtensionContextIdle(ctx: ExtensionContext): boolean;
|
|
71
|
+
export declare function hasExtensionContextPendingMessages(ctx: ExtensionContext): boolean;
|
|
72
|
+
export declare function compactExtensionContext(ctx: ExtensionContext, callbacks: Parameters<ExtensionContext["compact"]>[0]): ReturnType<ExtensionContext["compact"]>;
|
package/dist/lib/pi.js
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pi SDK adapter boundary
|
|
3
|
+
* Zones: pi agent sdk boundary, shared adapters
|
|
4
|
+
* Owns direct pi SDK imports and exposes narrow bridge-facing helpers/types for the extension composition layer
|
|
5
|
+
*/
|
|
6
|
+
import { SettingsManager, } from "@earendil-works/pi-coding-agent";
|
|
7
|
+
function isPiRunMode(value) {
|
|
8
|
+
return (value === "tui" || value === "rpc" || value === "json" || value === "print");
|
|
9
|
+
}
|
|
10
|
+
export function getExtensionContextMode(ctx) {
|
|
11
|
+
const mode = typeof ctx === "object" && ctx !== null
|
|
12
|
+
? ctx.mode
|
|
13
|
+
: undefined;
|
|
14
|
+
return isPiRunMode(mode) ? mode : undefined;
|
|
15
|
+
}
|
|
16
|
+
export function isExtensionContextPassiveRunMode(ctx) {
|
|
17
|
+
const mode = getExtensionContextMode(ctx);
|
|
18
|
+
return mode === "print" || mode === "json";
|
|
19
|
+
}
|
|
20
|
+
export function canStartPollingInExtensionContext(ctx) {
|
|
21
|
+
return !isExtensionContextPassiveRunMode(ctx);
|
|
22
|
+
}
|
|
23
|
+
export function formatPollingStartBlockedByRunMode(ctx) {
|
|
24
|
+
const mode = getExtensionContextMode(ctx);
|
|
25
|
+
return mode
|
|
26
|
+
? `Telegram polling is unavailable in Pi ${mode} mode. Use /telegram-connect from a long-lived Pi session.`
|
|
27
|
+
: "Telegram polling is unavailable in this Pi run mode.";
|
|
28
|
+
}
|
|
29
|
+
export function getSessionCompactionReason(event) {
|
|
30
|
+
const reason = event && typeof event === "object" && "reason" in event
|
|
31
|
+
? event.reason
|
|
32
|
+
: undefined;
|
|
33
|
+
return reason === "manual" || reason === "threshold" || reason === "overflow"
|
|
34
|
+
? reason
|
|
35
|
+
: "unknown";
|
|
36
|
+
}
|
|
37
|
+
export function createExtensionApiRuntimePorts(api) {
|
|
38
|
+
return {
|
|
39
|
+
sendUserMessage: (content, options) => api.sendUserMessage(content, options),
|
|
40
|
+
exec: (command, args, options) => api.exec(command, args, options),
|
|
41
|
+
getCommands: () => api.getCommands(),
|
|
42
|
+
getThinkingLevel: () => api.getThinkingLevel(),
|
|
43
|
+
setThinkingLevel: (level) => api.setThinkingLevel(level),
|
|
44
|
+
getActiveTools: () => api.getActiveTools(),
|
|
45
|
+
setActiveTools: (names) => api.setActiveTools(names),
|
|
46
|
+
setModel: (model) => api.setModel(model),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function readEnabledModels(value) {
|
|
50
|
+
if (value === undefined)
|
|
51
|
+
return undefined;
|
|
52
|
+
if (Array.isArray(value) && value.every((entry) => typeof entry === "string")) {
|
|
53
|
+
return [...value];
|
|
54
|
+
}
|
|
55
|
+
throw new TypeError("Host settings enabledModels must be a string array or undefined.");
|
|
56
|
+
}
|
|
57
|
+
export function normalizeSettingsManager(manager) {
|
|
58
|
+
if (typeof manager !== "object" || manager === null) {
|
|
59
|
+
throw new TypeError("Host settings manager must be an object.");
|
|
60
|
+
}
|
|
61
|
+
const host = manager;
|
|
62
|
+
if (typeof host.flush !== "function") {
|
|
63
|
+
throw new TypeError("Host settings manager must provide flush().");
|
|
64
|
+
}
|
|
65
|
+
const read = typeof host.getEnabledModels === "function"
|
|
66
|
+
? () => host.getEnabledModels.call(host)
|
|
67
|
+
: typeof host.get === "function"
|
|
68
|
+
? () => host.get.call(host, "enabledModels")
|
|
69
|
+
: undefined;
|
|
70
|
+
const write = typeof host.setEnabledModels === "function"
|
|
71
|
+
? (patterns) => host.setEnabledModels.call(host, patterns)
|
|
72
|
+
: typeof host.set === "function"
|
|
73
|
+
? (patterns) => host.set.call(host, "enabledModels", patterns ?? [])
|
|
74
|
+
: undefined;
|
|
75
|
+
if (!read || !write) {
|
|
76
|
+
throw new TypeError("Host settings manager must provide enabled-model read and write capabilities.");
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
reload: async () => {
|
|
80
|
+
await host.reload?.call(host);
|
|
81
|
+
},
|
|
82
|
+
flush: async () => {
|
|
83
|
+
await host.flush.call(host);
|
|
84
|
+
},
|
|
85
|
+
getEnabledModels: () => readEnabledModels(read()),
|
|
86
|
+
setEnabledModels: write,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
export async function createSettingsManager(cwd) {
|
|
90
|
+
// Pi returns its legacy settings surface synchronously. Compatible hosts may
|
|
91
|
+
// resolve a generic settings service asynchronously; normalize both once at
|
|
92
|
+
// the SDK boundary instead of leaking host distinctions into menu domains.
|
|
93
|
+
const factory = SettingsManager;
|
|
94
|
+
return normalizeSettingsManager(await factory.create(cwd));
|
|
95
|
+
}
|
|
96
|
+
export function createScopedModelPatternPersister(deps) {
|
|
97
|
+
return async (patterns, ctx) => {
|
|
98
|
+
const settingsManager = await deps.createSettingsManager(ctx.cwd);
|
|
99
|
+
settingsManager.setEnabledModels(patterns.length > 0 ? patterns : undefined);
|
|
100
|
+
await settingsManager.flush();
|
|
101
|
+
deps.clearCachedModelMenuInputs();
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
export function getExtensionContextModel(ctx) {
|
|
105
|
+
return ctx.model;
|
|
106
|
+
}
|
|
107
|
+
export function getExtensionContextCwd(ctx) {
|
|
108
|
+
return ctx.cwd;
|
|
109
|
+
}
|
|
110
|
+
export function getExtensionContextSessionId(ctx) {
|
|
111
|
+
return ctx.sessionManager.getSessionId();
|
|
112
|
+
}
|
|
113
|
+
export function isExtensionContextIdle(ctx) {
|
|
114
|
+
return ctx.isIdle();
|
|
115
|
+
}
|
|
116
|
+
export function hasExtensionContextPendingMessages(ctx) {
|
|
117
|
+
return ctx.hasPendingMessages();
|
|
118
|
+
}
|
|
119
|
+
export function compactExtensionContext(ctx, callbacks) {
|
|
120
|
+
return ctx.compact(callbacks);
|
|
121
|
+
}
|