@coinseeker/opencode-telegram-plugin 1.0.7 → 1.0.8

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/README.md CHANGED
@@ -15,11 +15,11 @@ Configure the npm package in `~/.config/opencode/opencode.json`:
15
15
 
16
16
  ```json
17
17
  {
18
- "plugin": ["@coinseeker/opencode-telegram-plugin@1.0.7"]
18
+ "plugin": ["@coinseeker/opencode-telegram-plugin@1.0.8"]
19
19
  }
20
20
  ```
21
21
 
22
- Current stable version: `@coinseeker/opencode-telegram-plugin@1.0.7`.
22
+ Current stable version: `@coinseeker/opencode-telegram-plugin@1.0.8`.
23
23
 
24
24
  Restart OpenCode after editing the config. OpenCode resolves npm package plugins on startup.
25
25
 
@@ -956,6 +956,33 @@ async function handleSessionError(event, ctx) {
956
956
  // src/events/start-work.ts
957
957
  var CALLBACK_RE3 = /^sw:(.+)$/;
958
958
  var START_WORK_COMMAND = "start-work";
959
+ var START_WORK_RE = /(?:^|[\s`])\/start-work(?:\s+([^\n`]+))?/g;
960
+ var StartWorkCommandStore = class {
961
+ commands = /* @__PURE__ */ new Map();
962
+ updateFromText(sessionID, text) {
963
+ const command = extractStartWorkCommand(sessionID, text);
964
+ if (!command) return void 0;
965
+ this.commands.set(sessionID, command.arguments);
966
+ return command;
967
+ }
968
+ get(sessionID) {
969
+ const args = this.commands.get(sessionID);
970
+ if (args === void 0) return void 0;
971
+ return { sessionID, arguments: args };
972
+ }
973
+ delete(sessionID) {
974
+ this.commands.delete(sessionID);
975
+ }
976
+ };
977
+ function extractStartWorkCommand(sessionID, text) {
978
+ let latestArgs;
979
+ for (const match of text.matchAll(START_WORK_RE)) {
980
+ const args = (match[1] ?? "").trim();
981
+ if (args) latestArgs = args;
982
+ }
983
+ if (latestArgs === void 0) return void 0;
984
+ return { sessionID, arguments: latestArgs };
985
+ }
959
986
  function startWorkCallbackData(sessionID) {
960
987
  const data = `sw:${encodeURIComponent(sessionID)}`;
961
988
  return Buffer.byteLength(data, "utf8") <= 64 ? data : void 0;
@@ -971,14 +998,25 @@ function createStartWorkDispatcher(ctx) {
971
998
  const match = CALLBACK_RE3.exec(data);
972
999
  if (!match) return;
973
1000
  const sessionID = decodeURIComponent(match[1]);
1001
+ const command = ctx.startWorkCommands.get(sessionID);
1002
+ if (!command) {
1003
+ await ctx.bot.editMessageRemoveKeyboard(
1004
+ messageId,
1005
+ `\u26A0\uFE0F No /start-work command was detected for this session.
1006
+
1007
+ Session: ${sessionID}`
1008
+ );
1009
+ return;
1010
+ }
974
1011
  try {
975
- await ctx.runSessionCommand(sessionID, START_WORK_COMMAND);
1012
+ await ctx.runSessionCommand(sessionID, START_WORK_COMMAND, command.arguments);
976
1013
  await ctx.bot.editMessageRemoveKeyboard(
977
1014
  messageId,
978
- `\u25B6\uFE0F Sent /start-work to opencode.
1015
+ `\u25B6\uFE0F Sent /start-work ${command.arguments} to opencode.
979
1016
 
980
1017
  Session: ${sessionID}`
981
1018
  );
1019
+ ctx.startWorkCommands.delete(sessionID);
982
1020
  ctx.logger.info("start-work command sent", { sessionID });
983
1021
  } catch (err) {
984
1022
  await ctx.bot.editMessageRemoveKeyboard(
@@ -1039,13 +1077,15 @@ async function sendIdleNotification(sessionId, ctx) {
1039
1077
  });
1040
1078
  if (!claimed) return;
1041
1079
  const title = ctx.sessionTitleService.getSessionTitle(sessionId);
1042
- const message = title ? `Agent has finished: ${title}
1080
+ const startWorkCommand = ctx.startWorkCommands.get(sessionId);
1081
+ const message = title ? `Agent has finished: ${title}` : "Agent has finished.";
1082
+ const keyboard = startWorkCommand ? startWorkKeyboard(sessionId) : void 0;
1083
+ const text = startWorkCommand ? `${message}
1043
1084
 
1044
- If this was a plan builder session, tap below to run /start-work.` : "Agent has finished.\n\nIf this was a plan builder session, tap below to run /start-work.";
1045
- const keyboard = startWorkKeyboard(sessionId);
1085
+ Plan is ready. Tap below to run /start-work ${startWorkCommand.arguments}.` : message;
1046
1086
  try {
1047
1087
  await ctx.bot.sendMessage(
1048
- message,
1088
+ text,
1049
1089
  keyboard ? { reply_markup: { inline_keyboard: keyboard } } : void 0
1050
1090
  );
1051
1091
  ctx.sessionTitleService.clearDeferredIdleNotification(sessionId);
@@ -1427,6 +1467,16 @@ var SessionTitleService = class {
1427
1467
 
1428
1468
  // src/telegram-remote.ts
1429
1469
  var pluginDir = dirname4(fileURLToPath(import.meta.url));
1470
+ function getTextPartFromMessagePartUpdated(event) {
1471
+ if (event.type !== "message.part.updated") return void 0;
1472
+ const part = event.properties?.part;
1473
+ if (!part || typeof part !== "object") return void 0;
1474
+ const candidate = part;
1475
+ if (candidate.type !== "text" || typeof candidate.sessionID !== "string" || typeof candidate.text !== "string") {
1476
+ return void 0;
1477
+ }
1478
+ return { type: "text", sessionID: candidate.sessionID, text: candidate.text };
1479
+ }
1430
1480
  var TelegramRemote = async (input) => {
1431
1481
  const logger = createLogger({ namespace: "telegram" });
1432
1482
  try {
@@ -1440,6 +1490,7 @@ var TelegramRemote = async (input) => {
1440
1490
  const claimsDir = join6(tmpdir4(), `opencoder-telegram-claims-${tokenHash}`);
1441
1491
  const pendingQuestions = createPendingQuestionStore({ tokenHash });
1442
1492
  const pendingPermissions = createPendingPermissionStore({ tokenHash });
1493
+ const startWorkCommands = new StartWorkCommandStore();
1443
1494
  const lockResult = await acquireLock({ lockPath });
1444
1495
  const isLeader = lockResult.acquired;
1445
1496
  logger.info(
@@ -1478,10 +1529,10 @@ var TelegramRemote = async (input) => {
1478
1529
  throwOnError: true
1479
1530
  });
1480
1531
  };
1481
- const runSessionCommand = async (sessionID, command) => {
1532
+ const runSessionCommand = async (sessionID, command, args) => {
1482
1533
  await input.client.session.command({
1483
1534
  path: { id: sessionID },
1484
- body: { command, arguments: "" },
1535
+ body: { command, arguments: args },
1485
1536
  throwOnError: true
1486
1537
  });
1487
1538
  };
@@ -1529,6 +1580,7 @@ var TelegramRemote = async (input) => {
1529
1580
  tokenHash,
1530
1581
  pendingQuestions,
1531
1582
  pendingPermissions,
1583
+ startWorkCommands,
1532
1584
  replyToQuestion,
1533
1585
  replyToPermission,
1534
1586
  runSessionCommand
@@ -1554,6 +1606,17 @@ var TelegramRemote = async (input) => {
1554
1606
  case "permission.updated":
1555
1607
  return handlePermissionUpdated(event, ctx);
1556
1608
  default: {
1609
+ const textPart = getTextPartFromMessagePartUpdated(extEvent);
1610
+ if (textPart) {
1611
+ const command = startWorkCommands.updateFromText(textPart.sessionID, textPart.text);
1612
+ if (command) {
1613
+ logger.info("start-work command detected", {
1614
+ sessionID: command.sessionID,
1615
+ arguments: command.arguments
1616
+ });
1617
+ }
1618
+ return;
1619
+ }
1557
1620
  if (isEventPermissionAsked(extEvent)) {
1558
1621
  if (!isLeader) return;
1559
1622
  return handlePermissionAsked(extEvent, ctx);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coinseeker/opencode-telegram-plugin",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "description": "Control and monitor OpenCode from Telegram with notifications, question replies, and subagent-aware completion.",
5
5
  "type": "module",
6
6
  "main": "dist/telegram-remote.js",