@coinseeker/opencode-telegram-plugin 1.1.1 → 1.1.3

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,14 +15,16 @@ Configure the npm package in `~/.config/opencode/opencode.json`:
15
15
 
16
16
  ```json
17
17
  {
18
- "plugin": ["@coinseeker/opencode-telegram-plugin@1.1.1"]
18
+ "plugin": ["@coinseeker/opencode-telegram-plugin@1.1.3"]
19
19
  }
20
20
  ```
21
21
 
22
- Current stable version: `@coinseeker/opencode-telegram-plugin@1.1.1`.
22
+ Current stable version: `@coinseeker/opencode-telegram-plugin@1.1.3`.
23
23
 
24
24
  Restart OpenCode after editing the config. OpenCode resolves npm package plugins on startup.
25
25
 
26
+ To update an existing install, replace the previous pinned package entry with `@coinseeker/opencode-telegram-plugin@1.1.3`, keep the rest of the `plugin` array unchanged, and restart OpenCode.
27
+
26
28
  ## Configure Telegram
27
29
 
28
30
  Create `~/.config/opencode/telegram-remote/.env`:
@@ -384,6 +384,8 @@ function parsePending(text) {
384
384
  throw new Error("Invalid pending permission: requestID");
385
385
  if (typeof parsed.sessionID !== "string")
386
386
  throw new Error("Invalid pending permission: sessionID");
387
+ if (parsed.directory !== void 0 && typeof parsed.directory !== "string")
388
+ throw new Error("Invalid pending permission: directory");
387
389
  if (typeof parsed.title !== "string") throw new Error("Invalid pending permission: title");
388
390
  if (typeof parsed.permission !== "string")
389
391
  throw new Error("Invalid pending permission: permission");
@@ -537,10 +539,34 @@ function replyLabel(reply) {
537
539
  if (reply === "always") return "Always allowed";
538
540
  return "Rejected";
539
541
  }
542
+ async function upgradeLegacyPendingPermission(permission, ctx) {
543
+ const found = await ctx.pendingPermissions.findByRequestID(
544
+ permission.requestID,
545
+ permission.sessionID,
546
+ ctx.serverUrl.href
547
+ );
548
+ if (!found || found.data.endpoint === "request") return;
549
+ await ctx.pendingPermissions.savePending(found.shortHash, {
550
+ ...found.data,
551
+ directory: ctx.directory,
552
+ title: permission.title,
553
+ permission: permission.permission,
554
+ patterns: permission.patterns,
555
+ always: permission.always,
556
+ endpoint: "request"
557
+ });
558
+ ctx.logger.info("permission pending upgraded to request endpoint", {
559
+ requestID: permission.requestID,
560
+ sessionID: permission.sessionID
561
+ });
562
+ }
540
563
  async function handleNormalizedPermission(permission, ctx) {
541
564
  const permissionKey = `${ctx.serverUrl.href}:${permission.sessionID}:${permission.requestID}`;
542
565
  const claimed = await claimOnce({ claimsDir: ctx.claimsDir, key: `permission:${permissionKey}` });
543
- if (!claimed) return;
566
+ if (!claimed) {
567
+ if (permission.endpoint === "request") await upgradeLegacyPendingPermission(permission, ctx);
568
+ return;
569
+ }
544
570
  const shortHash = createPermissionShortHash(
545
571
  permission.requestID,
546
572
  permission.sessionID,
@@ -558,6 +584,7 @@ async function handleNormalizedPermission(permission, ctx) {
558
584
  requestID: permission.requestID,
559
585
  sessionID: permission.sessionID,
560
586
  serverUrl: ctx.serverUrl.href,
587
+ directory: ctx.directory,
561
588
  title: permission.title,
562
589
  permission: permission.permission,
563
590
  patterns: permission.patterns,
@@ -641,7 +668,8 @@ function createPermissionDispatcher(ctx) {
641
668
  pending.sessionID,
642
669
  reply,
643
670
  pending.endpoint,
644
- pending.serverUrl
671
+ pending.serverUrl,
672
+ pending.directory
645
673
  );
646
674
  await ctx.bot.editMessageRemoveKeyboard(
647
675
  messageId,
@@ -661,7 +689,10 @@ ${pending.permission}: ${pending.title}`
661
689
  );
662
690
  ctx.logger.error("failed to send permission reply", {
663
691
  error: String(err),
664
- requestID: pending.requestID
692
+ requestID: pending.requestID,
693
+ endpoint: pending.endpoint,
694
+ serverUrl: pending.serverUrl,
695
+ directory: pending.directory
665
696
  });
666
697
  } finally {
667
698
  await ctx.pendingPermissions.deletePending(shortHash);
@@ -1560,7 +1591,7 @@ var ROOT_IDLE_RECHECK_DELAY_MS = 2500;
1560
1591
  var DEFERRED_PARENT_CONFIRM_DELAY_MS = 2500;
1561
1592
  var deferredConfirmTimers = /* @__PURE__ */ new Map();
1562
1593
  function sleep(ms) {
1563
- return new Promise((resolve) => setTimeout(resolve, ms));
1594
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
1564
1595
  }
1565
1596
  function agentFinishedMessage(title, agent) {
1566
1597
  const base = title ? `Agent has finished: ${title}` : "Agent has finished.";
@@ -1913,48 +1944,170 @@ ${body}
1913
1944
 
1914
1945
  // src/lib/plan-readiness.ts
1915
1946
  import { access, readFile as readFile5, readdir as readdir6, stat as stat2 } from "fs/promises";
1916
- import { join as join6 } from "path";
1917
- async function checkPlanReadiness(args) {
1918
- const { projectRoot } = args;
1919
- const omoDir = join6(projectRoot, ".omo");
1920
- const plansDir = join6(omoDir, "plans");
1921
- const boulderPath = join6(omoDir, "boulder.json");
1947
+ import { basename, isAbsolute, join as join6, relative, resolve } from "path";
1948
+ function asRecord2(value) {
1949
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
1950
+ return value;
1951
+ }
1952
+ function stringArray(value) {
1953
+ if (!Array.isArray(value)) return [];
1954
+ return value.filter((item) => typeof item === "string");
1955
+ }
1956
+ function optionalString(value) {
1957
+ return typeof value === "string" ? value : void 0;
1958
+ }
1959
+ function normalizeBoulderWork(value) {
1960
+ const record = asRecord2(value);
1961
+ if (!record || typeof record.active_plan !== "string") return void 0;
1962
+ const work = {
1963
+ activePlan: record.active_plan,
1964
+ sessionIds: stringArray(record.session_ids)
1965
+ };
1966
+ const planName = optionalString(record.plan_name);
1967
+ if (planName !== void 0) work.planName = planName;
1968
+ const status = optionalString(record.status);
1969
+ if (status !== void 0) work.status = status;
1970
+ const startedAt = optionalString(record.started_at);
1971
+ if (startedAt !== void 0) work.startedAt = startedAt;
1972
+ const updatedAt = optionalString(record.updated_at);
1973
+ if (updatedAt !== void 0) work.updatedAt = updatedAt;
1974
+ const worktreePath = optionalString(record.worktree_path);
1975
+ if (worktreePath !== void 0) work.worktreePath = worktreePath;
1976
+ return work;
1977
+ }
1978
+ function normalizeBoulderState(value) {
1979
+ const record = asRecord2(value);
1980
+ if (!record) return void 0;
1981
+ const state = { sessionIds: stringArray(record.session_ids) };
1982
+ const activePlan = optionalString(record.active_plan);
1983
+ if (activePlan !== void 0) state.activePlan = activePlan;
1984
+ const planName = optionalString(record.plan_name);
1985
+ if (planName !== void 0) state.planName = planName;
1986
+ const status = optionalString(record.status);
1987
+ if (status !== void 0) state.status = status;
1988
+ const startedAt = optionalString(record.started_at);
1989
+ if (startedAt !== void 0) state.startedAt = startedAt;
1990
+ const updatedAt = optionalString(record.updated_at);
1991
+ if (updatedAt !== void 0) state.updatedAt = updatedAt;
1992
+ const worktreePath = optionalString(record.worktree_path);
1993
+ if (worktreePath !== void 0) state.worktreePath = worktreePath;
1994
+ const activeWorkId = optionalString(record.active_work_id);
1995
+ if (activeWorkId !== void 0) state.activeWorkId = activeWorkId;
1996
+ const worksRecord = asRecord2(record.works);
1997
+ if (worksRecord) {
1998
+ const works = {};
1999
+ for (const [workId, rawWork] of Object.entries(worksRecord)) {
2000
+ const work = normalizeBoulderWork(rawWork);
2001
+ if (work) works[workId] = work;
2002
+ }
2003
+ if (Object.keys(works).length > 0) state.works = works;
2004
+ }
2005
+ return state;
2006
+ }
2007
+ async function readBoulderState(boulderPath) {
2008
+ let text;
1922
2009
  try {
1923
- await access(omoDir);
2010
+ text = await readFile5(boulderPath, "utf8");
2011
+ } catch (err) {
2012
+ if (err instanceof Error && "code" in err && err.code === "ENOENT") {
2013
+ return { exists: false };
2014
+ }
2015
+ return { exists: true };
2016
+ }
2017
+ try {
2018
+ return { exists: true, state: normalizeBoulderState(JSON.parse(text)) };
1924
2019
  } catch {
1925
- return {
1926
- ready: false,
1927
- reason: "no-omo-dir",
1928
- detail: `${omoDir} does not exist`
1929
- };
2020
+ return { exists: true };
1930
2021
  }
2022
+ }
2023
+ function mirrorWorkFromState(state) {
2024
+ if (!state.activePlan) return void 0;
2025
+ const work = {
2026
+ activePlan: state.activePlan,
2027
+ sessionIds: state.sessionIds
2028
+ };
2029
+ if (state.planName !== void 0) work.planName = state.planName;
2030
+ if (state.status !== void 0) work.status = state.status;
2031
+ if (state.startedAt !== void 0) work.startedAt = state.startedAt;
2032
+ if (state.updatedAt !== void 0) work.updatedAt = state.updatedAt;
2033
+ if (state.worktreePath !== void 0) work.worktreePath = state.worktreePath;
2034
+ return work;
2035
+ }
2036
+ function boulderWorks(state) {
2037
+ if (state.works) return Object.values(state.works);
2038
+ const mirrorWork = mirrorWorkFromState(state);
2039
+ return mirrorWork ? [mirrorWork] : [];
2040
+ }
2041
+ function parseIsoToMs(value) {
2042
+ if (!value) return 0;
2043
+ const ms = Date.parse(value);
2044
+ return Number.isNaN(ms) ? 0 : ms;
2045
+ }
2046
+ function findBoulderWorkForSession(state, sessionId) {
2047
+ const works = boulderWorks(state).filter((work) => work.sessionIds.includes(sessionId)).sort(
2048
+ (left, right) => parseIsoToMs(right.updatedAt ?? right.startedAt) - parseIsoToMs(left.updatedAt ?? left.startedAt)
2049
+ );
2050
+ if (works[0]) return works[0];
2051
+ const mirrorWork = mirrorWorkFromState(state);
2052
+ if (mirrorWork && state.sessionIds.includes(sessionId)) return mirrorWork;
2053
+ return void 0;
2054
+ }
2055
+ function isActiveBoulderWork(work) {
2056
+ return work.status !== "completed" && work.status !== "abandoned";
2057
+ }
2058
+ function resolveTrackedPath(baseDirectory, trackedPath) {
2059
+ return isAbsolute(trackedPath) ? resolve(trackedPath) : resolve(baseDirectory, trackedPath);
2060
+ }
2061
+ async function resolveBoulderPlanPath(projectRoot, work) {
2062
+ const absolutePlanPath = resolveTrackedPath(projectRoot, work.activePlan);
2063
+ const worktreePath = work.worktreePath?.trim();
2064
+ if (!worktreePath) return absolutePlanPath;
2065
+ const relativePlanPath = relative(resolve(projectRoot), absolutePlanPath);
2066
+ if (relativePlanPath.length === 0 || relativePlanPath.startsWith("..") || isAbsolute(relativePlanPath)) {
2067
+ return absolutePlanPath;
2068
+ }
2069
+ const worktreePlanPath = resolve(resolveTrackedPath(projectRoot, worktreePath), relativePlanPath);
1931
2070
  try {
1932
- await access(boulderPath);
1933
- return {
1934
- ready: false,
1935
- reason: "boulder-active",
1936
- detail: `${boulderPath} exists`
1937
- };
2071
+ await access(worktreePlanPath);
2072
+ return worktreePlanPath;
1938
2073
  } catch {
2074
+ return absolutePlanPath;
1939
2075
  }
2076
+ }
2077
+ function planNameFromPath(planPath) {
2078
+ return basename(planPath, ".md");
2079
+ }
2080
+ function normalizePlanToken(value) {
2081
+ return value.normalize("NFKD").toLowerCase().replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9가-힣]+/g, "-").replace(/^-+|-+$/g, "");
2082
+ }
2083
+ function selectPlanByHint(candidates, planHint) {
2084
+ if (!planHint) return void 0;
2085
+ const normalizedHint = normalizePlanToken(planHint);
2086
+ if (!normalizedHint) return void 0;
2087
+ return candidates.find((candidate) => {
2088
+ const planName = candidate.name.replace(/\.md$/, "");
2089
+ return normalizePlanToken(planName) === normalizedHint;
2090
+ });
2091
+ }
2092
+ function resolvePlanPathHint(projectRoot, planPath) {
2093
+ if (!planPath) return void 0;
2094
+ const resolvedPath = isAbsolute(planPath) ? resolve(planPath) : resolve(projectRoot, planPath);
2095
+ const plansRoot = resolve(projectRoot, ".omo", "plans");
2096
+ const relativePlanPath = relative(plansRoot, resolvedPath);
2097
+ if (!resolvedPath.endsWith(".md") || relativePlanPath.length === 0 || relativePlanPath.startsWith("..") || isAbsolute(relativePlanPath)) {
2098
+ return void 0;
2099
+ }
2100
+ return resolvedPath;
2101
+ }
2102
+ async function getPlanFiles(plansDir) {
1940
2103
  let planFiles = [];
1941
2104
  try {
1942
2105
  const entries = await readdir6(plansDir);
1943
2106
  planFiles = entries.filter((e) => e.endsWith(".md"));
1944
2107
  } catch {
1945
- return {
1946
- ready: false,
1947
- reason: "no-plans",
1948
- detail: `${plansDir} not found or empty`
1949
- };
1950
- }
1951
- if (planFiles.length === 0) {
1952
- return {
1953
- ready: false,
1954
- reason: "no-plans",
1955
- detail: `No .md files in ${plansDir}`
1956
- };
2108
+ return void 0;
1957
2109
  }
2110
+ if (planFiles.length === 0) return [];
1958
2111
  const stats = await Promise.all(
1959
2112
  planFiles.map(async (f) => {
1960
2113
  const full = join6(plansDir, f);
@@ -1962,9 +2115,20 @@ async function checkPlanReadiness(args) {
1962
2115
  return { path: full, name: f, mtime: s.mtime.getTime() };
1963
2116
  })
1964
2117
  );
1965
- stats.sort((a, b) => b.mtime - a.mtime);
1966
- const latest = stats[0];
1967
- const content = await readFile5(latest.path, "utf8");
2118
+ return stats.sort((a, b) => b.mtime - a.mtime);
2119
+ }
2120
+ async function readPlanProgress(planPath, planName, boulderActive = false) {
2121
+ let content;
2122
+ try {
2123
+ content = await readFile5(planPath, "utf8");
2124
+ } catch {
2125
+ return {
2126
+ ready: false,
2127
+ reason: "no-plans",
2128
+ detail: `${planPath} not found`,
2129
+ ...boulderActive ? { boulderActive } : {}
2130
+ };
2131
+ }
1968
2132
  const totalMatches = content.match(/^- \[[ xX]\]/gm) ?? [];
1969
2133
  const completedMatches = content.match(/^- \[[xX]\]/gm) ?? [];
1970
2134
  const total = totalMatches.length;
@@ -1973,24 +2137,99 @@ async function checkPlanReadiness(args) {
1973
2137
  return {
1974
2138
  ready: false,
1975
2139
  reason: "plan-empty",
1976
- detail: `${latest.name}: no checkboxes found`
2140
+ detail: `${planName}: no checkboxes found`,
2141
+ ...boulderActive ? { boulderActive } : {}
1977
2142
  };
1978
2143
  }
1979
2144
  if (completed >= total) {
1980
2145
  return {
1981
2146
  ready: false,
1982
2147
  reason: "all-plans-complete",
1983
- detail: `${latest.name}: ${completed}/${total} complete`
2148
+ detail: `${planName}: ${completed}/${total} complete`,
2149
+ ...boulderActive ? { boulderActive } : {}
1984
2150
  };
1985
2151
  }
1986
2152
  return {
1987
2153
  ready: true,
1988
- planPath: latest.path,
1989
- planName: latest.name.replace(/\.md$/, ""),
2154
+ planPath,
2155
+ planName,
1990
2156
  total,
1991
- completed
2157
+ completed,
2158
+ ...boulderActive ? { boulderActive } : {}
1992
2159
  };
1993
2160
  }
2161
+ async function checkPlanReadiness(args) {
2162
+ const { projectRoot, sessionId } = args;
2163
+ const allowLatestFallback = args.allowLatestFallback ?? sessionId === void 0;
2164
+ const omoDir = join6(projectRoot, ".omo");
2165
+ const plansDir = join6(omoDir, "plans");
2166
+ const boulderPath = join6(omoDir, "boulder.json");
2167
+ try {
2168
+ await access(omoDir);
2169
+ } catch {
2170
+ return {
2171
+ ready: false,
2172
+ reason: "no-omo-dir",
2173
+ detail: `${omoDir} does not exist`
2174
+ };
2175
+ }
2176
+ const boulder = await readBoulderState(boulderPath);
2177
+ const projectBoulderActive = boulder.exists;
2178
+ if (boulder.exists && sessionId === void 0) {
2179
+ return {
2180
+ ready: false,
2181
+ reason: "boulder-active",
2182
+ detail: `${boulderPath} exists`,
2183
+ boulderActive: true
2184
+ };
2185
+ }
2186
+ if (boulder.state && sessionId !== void 0) {
2187
+ const work = findBoulderWorkForSession(boulder.state, sessionId);
2188
+ if (work) {
2189
+ const planPath = await resolveBoulderPlanPath(projectRoot, work);
2190
+ return readPlanProgress(
2191
+ planPath,
2192
+ work.planName ?? planNameFromPath(planPath),
2193
+ isActiveBoulderWork(work)
2194
+ );
2195
+ }
2196
+ }
2197
+ const explicitPlanPath = resolvePlanPathHint(projectRoot, args.planPath);
2198
+ if (explicitPlanPath) {
2199
+ return readPlanProgress(explicitPlanPath, planNameFromPath(explicitPlanPath), projectBoulderActive);
2200
+ }
2201
+ const stats = await getPlanFiles(plansDir);
2202
+ if (stats === void 0) {
2203
+ return {
2204
+ ready: false,
2205
+ reason: "no-plans",
2206
+ detail: `${plansDir} not found or empty`,
2207
+ ...projectBoulderActive ? { boulderActive: true } : {}
2208
+ };
2209
+ }
2210
+ if (stats.length === 0) {
2211
+ return {
2212
+ ready: false,
2213
+ reason: "no-plans",
2214
+ detail: `No .md files in ${plansDir}`,
2215
+ ...projectBoulderActive ? { boulderActive: true } : {}
2216
+ };
2217
+ }
2218
+ const hinted = selectPlanByHint(stats, args.planHint);
2219
+ if (hinted) {
2220
+ return readPlanProgress(hinted.path, hinted.name.replace(/\.md$/, ""), projectBoulderActive);
2221
+ }
2222
+ if (!allowLatestFallback) {
2223
+ return {
2224
+ ready: false,
2225
+ reason: "no-session-plan",
2226
+ detail: `No plan associated with session ${sessionId ?? "missing"}`,
2227
+ ...projectBoulderActive ? { boulderActive: true } : {}
2228
+ };
2229
+ }
2230
+ const latest = stats[0];
2231
+ return readPlanProgress(latest.path, latest.name.replace(/\.md$/, ""), projectBoulderActive);
2232
+ }
1994
2233
  async function recheckSessionIdle(client, sessionId) {
1995
2234
  const result = await client.session.status();
1996
2235
  const statuses = result.data ?? {};
@@ -2052,6 +2291,8 @@ function planReadinessKorean(result) {
2052
2291
  }
2053
2292
  case "boulder-active":
2054
2293
  return "boulder \uD65C\uC131";
2294
+ case "no-session-plan":
2295
+ return "\uC138\uC158 \uC5F0\uACB0 plan \uC5C6\uC74C";
2055
2296
  }
2056
2297
  }
2057
2298
  function planLine(result) {
@@ -2061,7 +2302,7 @@ function planLine(result) {
2061
2302
  return `<b>\uD50C\uB79C \uC0C1\uD0DC</b>: ${planReadinessKorean(result)}`;
2062
2303
  }
2063
2304
  function boulderLine(result) {
2064
- const active = !result.ready && result.reason === "boulder-active";
2305
+ const active = result.boulderActive === true || !result.ready && result.reason === "boulder-active";
2065
2306
  return active ? "<b>Boulder</b>: \uD65C\uC131" : "<b>Boulder</b>: \uC5C6\uC74C";
2066
2307
  }
2067
2308
  function createStatusDispatcher(deps) {
@@ -2162,11 +2403,18 @@ function createStatusDispatcher(deps) {
2162
2403
  return;
2163
2404
  }
2164
2405
  const projectRoot = resolveProjectRoot(session);
2165
- const planReady = await checkPlanReadiness({ projectRoot });
2406
+ const rawTitle = session.title ?? entry.title;
2407
+ const rawAgent = entry.agent ?? session.agent;
2408
+ const planReady = await checkPlanReadiness({
2409
+ projectRoot,
2410
+ sessionId: entry.sessionId,
2411
+ planHint: rawTitle,
2412
+ allowLatestFallback: rawAgent === "plan"
2413
+ });
2166
2414
  const userSnippet = buildSnippet(findLastByRole(messages, "user"));
2167
2415
  const assistantSnippet = buildSnippet(findLastByRole(messages, "assistant"));
2168
- const title = escapeHtml(session.title ?? "");
2169
- const agent = entry.agent ? escapeHtml(entry.agent) : "?";
2416
+ const title = escapeHtml(rawTitle ?? "");
2417
+ const agent = rawAgent ? escapeHtml(rawAgent) : "?";
2170
2418
  const text = [
2171
2419
  `<b>\uC138\uC158 #${n}</b>: ${title}`,
2172
2420
  `\uC5D0\uC774\uC804\uD2B8: ${agent}`,
@@ -2208,6 +2456,8 @@ function readinessMessage(reason) {
2208
2456
  return "plan \uC758 \uBAA8\uB4E0 task \uAC00 \uC644\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uC0C8 plan \uC791\uC131 \uD544\uC694";
2209
2457
  case "boulder-active":
2210
2458
  return ".omo/boulder.json \uC774 \uC774\uBBF8 \uC874\uC7AC\uD569\uB2C8\uB2E4. \uAE30\uC874 \uC791\uC5C5\uC774 \uC9C4\uD589 \uC911\uC774\uAC70\uB098 archive \uAC00 \uD544\uC694\uD569\uB2C8\uB2E4";
2459
+ case "no-session-plan":
2460
+ return "\uD574\uB2F9 \uC138\uC158\uACFC \uC5F0\uACB0\uB41C plan \uC774 \uC5C6\uC2B5\uB2C8\uB2E4";
2211
2461
  }
2212
2462
  }
2213
2463
  function isSessionNotFoundError(err) {
@@ -2878,6 +3128,12 @@ var SessionTitleService = class {
2878
3128
 
2879
3129
  // src/telegram-remote.ts
2880
3130
  var pluginDir = dirname6(fileURLToPath(import.meta.url));
3131
+ function withDirectoryQuery(path, directory) {
3132
+ if (directory === void 0) return path;
3133
+ const url = new URL(path, "http://opencode.local");
3134
+ url.searchParams.set("directory", directory);
3135
+ return `${url.pathname}${url.search}`;
3136
+ }
2881
3137
  async function postToServer(serverUrl, path, body) {
2882
3138
  const safeServerUrl = normalizeOpenCodeServerUrl(serverUrl);
2883
3139
  if (!safeServerUrl) throw new Error("Invalid OpenCode server URL");
@@ -2950,9 +3206,12 @@ var TelegramRemote = async (input) => {
2950
3206
  throwOnError: true
2951
3207
  });
2952
3208
  };
2953
- const replyToPermission = async (requestID, sessionID, reply, endpoint2, serverUrl = input.serverUrl.href) => {
3209
+ const replyToPermission = async (requestID, sessionID, reply, endpoint2, serverUrl = input.serverUrl.href, directory = input.directory) => {
2954
3210
  if (endpoint2 === "request") {
2955
- const path2 = `/permission/${encodeURIComponent(requestID)}/reply`;
3211
+ const path2 = withDirectoryQuery(
3212
+ `/permission/${encodeURIComponent(requestID)}/reply`,
3213
+ directory
3214
+ );
2956
3215
  if (serverUrl !== input.serverUrl.href) {
2957
3216
  await postToServer(serverUrl, path2, { reply });
2958
3217
  return;
@@ -2965,7 +3224,10 @@ var TelegramRemote = async (input) => {
2965
3224
  });
2966
3225
  return;
2967
3226
  }
2968
- const path = `/session/${encodeURIComponent(sessionID)}/permissions/${encodeURIComponent(requestID)}`;
3227
+ const path = withDirectoryQuery(
3228
+ `/session/${encodeURIComponent(sessionID)}/permissions/${encodeURIComponent(requestID)}`,
3229
+ directory
3230
+ );
2969
3231
  if (serverUrl !== input.serverUrl.href) {
2970
3232
  await postToServer(serverUrl, path, { response: reply });
2971
3233
  return;
@@ -3067,6 +3329,7 @@ var TelegramRemote = async (input) => {
3067
3329
  claimsDir,
3068
3330
  pluginDir,
3069
3331
  serverUrl: input.serverUrl,
3332
+ directory: input.directory,
3070
3333
  tokenHash,
3071
3334
  pendingQuestions,
3072
3335
  pendingPermissions,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coinseeker/opencode-telegram-plugin",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
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",