appback-remoteagent 0.23.2 → 0.23.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
@@ -105,7 +105,8 @@ Current command surface implemented in `src/bot.ts`:
105
105
  | `/new` | Creates and binds a new session using the saved default mode in a new managed workspace |
106
106
  | `/switch <session>` | Rebinds this chat to an existing RemoteAgent session |
107
107
  | `/status` | Shows current session, workspace, provider, and sandbox state |
108
- | `/model [name]` | Lists selectable provider models or changes the current session model. New Codex sessions default to `gpt-6-astra` with `medium` reasoning. Use `/model gpt-6-astra` for an existing session. |
108
+ | `/model [name]` | Lists selectable provider models or changes the current session model. New Codex sessions default to `gpt-6-astra`. Use `/model gpt-6-astra` for an existing session. |
109
+ | `/option reasoning [low\|medium\|high\|xhigh\|max]` | Shows or changes Astra reasoning for all bots on this server. Defaults to `medium`; persisted as `CODEX_REASONING_EFFORT`. Applies on the next execution without restarting. Running replies retain their actual execution effort in the header. |
109
110
  | `/sandbox [codex <mode>]` | Lists Codex sandbox choices or changes the current session sandbox |
110
111
  | `/option retry <count>` | Sets the automatic continuation turn limit and persists it to `~/.remoteagent/.env` |
111
112
  | `/option timeout <seconds>` | Sets the provider execution timeout and persists it to `~/.remoteagent/.env` |
@@ -3,6 +3,7 @@ import os from "node:os";
3
3
  import path from "node:path";
4
4
  import { randomUUID } from "node:crypto";
5
5
  import { spawnWithPlatformShell } from "./windows-shell.js";
6
+ import { getAstraReasoning } from "../services/codex-reasoning.js";
6
7
  export class CodexAdapter {
7
8
  codexBin;
8
9
  timeoutMs;
@@ -68,7 +69,7 @@ export class CodexAdapter {
68
69
  args.push("-m", request.model);
69
70
  }
70
71
  if (request.model === "gpt-6-astra") {
71
- args.push("-c", 'model_reasoning_effort="medium"');
72
+ args.push("-c", `model_reasoning_effort=${JSON.stringify(request.reasoningEffort ?? getAstraReasoning())}`);
72
73
  }
73
74
  this.appendSandboxArgs(args, sandboxMode);
74
75
  args.push("-o", outputPath, "-C", request.cwd);
@@ -86,7 +87,7 @@ export class CodexAdapter {
86
87
  args.push("-m", request.model);
87
88
  }
88
89
  if (request.model === "gpt-6-astra") {
89
- args.push("-c", 'model_reasoning_effort="medium"');
90
+ args.push("-c", `model_reasoning_effort=${JSON.stringify(request.reasoningEffort ?? getAstraReasoning())}`);
90
91
  }
91
92
  this.appendSandboxArgs(args, sandboxMode);
92
93
  args.push("-o", outputPath, request.sessionId);
package/dist/bot.js CHANGED
@@ -3,6 +3,7 @@ import fsSync from "node:fs";
3
3
  import fs from "node:fs/promises";
4
4
  import os from "node:os";
5
5
  import path from "node:path";
6
+ import process from "node:process";
6
7
  import { randomUUID } from "node:crypto";
7
8
  import { promisify } from "node:util";
8
9
  import { Bot, GrammyError, HttpError } from "grammy";
@@ -13,6 +14,7 @@ import { AgentMemoryService } from "./services/agent-memory-service.js";
13
14
  import { WorkspaceCleanupService } from "./services/workspace-cleanup-service.js";
14
15
  import { exportSecrets } from "./services/secret-transfer-service.js";
15
16
  import { deleteTelegramCommandMenu, setTelegramCommandMenu } from "./telegram-command-menu.js";
17
+ import { ASTRA_REASONING_LEVELS, getAstraReasoning, isAstraReasoning } from "./services/codex-reasoning.js";
16
18
  const execFileAsync = promisify(execFile);
17
19
  const HELP_TEXT = [
18
20
  "Commands:",
@@ -30,7 +32,7 @@ const HELP_TEXT = [
30
32
  "/stop",
31
33
  "/sandbox codex <read-only|workspace-write|danger-full-access>",
32
34
  "/status",
33
- "/option [retry <count>|timeout <seconds>|intent <count>|command-menu <on|off|refresh>]",
35
+ "/option [retry <count>|timeout <seconds>|intent <count>|reasoning <low|medium|high|xhigh|max>|command-menu <on|off|refresh>]",
34
36
  "/state [clear|note <text>]",
35
37
  "/artifacts list|cleanup <days>",
36
38
  "/cleanup",
@@ -642,12 +644,13 @@ ${bridge.formatStatus(mapping)}`);
642
644
  ],
643
645
  [
644
646
  actionButton(ctx, "Intent", { kind: "option.show", option: "intent" }),
647
+ actionButton(ctx, "Reasoning", { kind: "option.show", option: "reasoning" }),
645
648
  actionButton(ctx, "Command menu", { kind: "option.show", option: "command-menu" }),
646
649
  ],
647
650
  ]));
648
651
  return;
649
652
  }
650
- if (option !== "retry" && option !== "timeout" && option !== "intent" && option !== "command-menu") {
653
+ if (option !== "retry" && option !== "timeout" && option !== "intent" && option !== "reasoning" && option !== "command-menu") {
651
654
  await reply(ctx, "Usage: `/option retry <count>`, `/option timeout <seconds>`, `/option intent <count>`, or `/option command-menu <on|off|refresh>`\n\n`retry` controls automatic continuation turns. `timeout` controls one provider execution limit. `intent` controls retries for untagged intent-only provider replies. `command-menu` controls Telegram slash-command autocomplete for all configured bots.", {
652
655
  parse_mode: "Markdown",
653
656
  });
@@ -659,6 +662,17 @@ ${bridge.formatStatus(mapping)}`);
659
662
  });
660
663
  return;
661
664
  }
665
+ if (option === "reasoning") {
666
+ const effort = value.toLowerCase();
667
+ if (!isAstraReasoning(effort)) {
668
+ await reply(ctx, `Invalid reasoning effort. Use /option reasoning <${ASTRA_REASONING_LEVELS.join("|")}>.`);
669
+ return;
670
+ }
671
+ await upsertInstalledEnvValue("CODEX_REASONING_EFFORT", effort);
672
+ process.env.CODEX_REASONING_EFFORT = effort;
673
+ await reply(ctx, `Set Astra reasoning effort to ${effort}. Applies to the next Astra execution for all bots on this server; running executions keep their current effort.\n\nSaved: CODEX_REASONING_EFFORT=${effort}`);
674
+ return;
675
+ }
662
676
  if (option === "command-menu") {
663
677
  const action = value.toLowerCase();
664
678
  if (!["on", "off", "refresh"].includes(action)) {
@@ -2507,12 +2521,14 @@ function formatRuntimeOptions() {
2507
2521
  `- retry: ${formatRetryLimit(config.telegramAutoProgressMaxTurns)} (TELEGRAM_AUTO_PROGRESS_MAX_TURNS)`,
2508
2522
  `- timeout: ${formatTimeoutSeconds(config.commandTimeoutMs)} (COMMAND_TIMEOUT_MS)`,
2509
2523
  `- intent: ${formatRetryLimit(config.telegramUntaggedIntentRetries)} (TELEGRAM_UNTAGGED_INTENT_RETRIES)`,
2524
+ `- reasoning: ${getAstraReasoning()} (Astra, server-wide, CODEX_REASONING_EFFORT)`,
2510
2525
  `- command-menu: ${config.telegramCommandMenuEnabled ? "on" : "off"} (TELEGRAM_COMMAND_MENU_ENABLED)`,
2511
2526
  "",
2512
2527
  "Usage:",
2513
2528
  "/option retry <count>",
2514
2529
  "/option timeout <seconds>",
2515
2530
  "/option intent <count>",
2531
+ "/option reasoning <low|medium|high|xhigh|max>",
2516
2532
  "/option command-menu <on|off|refresh>",
2517
2533
  "",
2518
2534
  "`retry 0` disables the automatic continuation limit.",
@@ -2521,6 +2537,9 @@ function formatRuntimeOptions() {
2521
2537
  ].join("\n");
2522
2538
  }
2523
2539
  function formatRuntimeOptionDetail(option) {
2540
+ if (option === "reasoning") {
2541
+ return `Current Astra reasoning effort: ${getAstraReasoning()}\n\nUsage: /option reasoning <low|medium|high|xhigh|max>\nApplies to the next Astra execution for all bots on this server. No restart required.`;
2542
+ }
2524
2543
  if (option === "retry") {
2525
2544
  return `Current automatic continuation retry limit: ${formatRetryLimit(config.telegramAutoProgressMaxTurns)}\n\nUsage: \`/option retry <count>\``;
2526
2545
  }
@@ -2,6 +2,7 @@ import fs from "node:fs/promises";
2
2
  import { randomBytes } from "node:crypto";
3
3
  import os from "node:os";
4
4
  import path from "node:path";
5
+ import { getAstraReasoning } from "./codex-reasoning.js";
5
6
  import { stopSpawnedExecution } from "../adapters/windows-shell.js";
6
7
  import { CODEX_USAGE_FALLBACK_MODEL, CodexUsageFallbackService, parseCodexUsageLimit, } from "./codex-usage-fallback-service.js";
7
8
  const MODEL_PRESETS = {
@@ -367,7 +368,7 @@ export class BridgeService {
367
368
  return responses.map((response) => {
368
369
  const sessionLabel = response.publicSessionId ?? response.sessionId;
369
370
  const modelLabel = response.model?.trim() || this.defaultModelFor(response.provider);
370
- const effortLabel = response.provider === "codex" && modelLabel === "gpt-6-astra" ? " | medium" : "";
371
+ const effortLabel = response.provider === "codex" && modelLabel === "gpt-6-astra" ? ` | ${response.reasoningEffort ?? getAstraReasoning()}` : "";
371
372
  const header = `[${response.provider.toUpperCase()} | ${modelLabel}${effortLabel} | ${sessionLabel}]`;
372
373
  return `${header}\n${response.output}`;
373
374
  });
@@ -499,8 +500,10 @@ export class BridgeService {
499
500
  ? await this.codexUsageFallback.selectExecutionModel(primaryModel)
500
501
  : { model: primaryModel, recoveryProbe: false };
501
502
  let executionModel = selection.model;
503
+ const reasoningEffort = getAstraReasoning();
502
504
  const forwardProgress = async (output, model) => {
503
505
  const progressResponse = {
506
+ reasoningEffort: provider === "codex" && model === "gpt-6-astra" ? reasoningEffort : undefined,
504
507
  provider,
505
508
  sessionId: providerSession.sessionId ?? session.sessionId,
506
509
  publicSessionId: session.publicId,
@@ -521,6 +524,7 @@ export class BridgeService {
521
524
  await onProgress?.(progressResponse);
522
525
  };
523
526
  const sendWithModel = (model) => this.adapters[provider].send({
527
+ reasoningEffort: provider === "codex" && model === "gpt-6-astra" ? reasoningEffort : undefined,
524
528
  botId,
525
529
  chatId: chatId ?? requestSource,
526
530
  remoteSessionId: session.sessionId,
@@ -611,6 +615,7 @@ export class BridgeService {
611
615
  ...response,
612
616
  publicSessionId: session.publicId,
613
617
  model: executionModel,
618
+ reasoningEffort: provider === "codex" && executionModel === "gpt-6-astra" ? reasoningEffort : undefined,
614
619
  });
615
620
  }
616
621
  return responses;
@@ -0,0 +1,8 @@
1
+ export const ASTRA_REASONING_LEVELS = ["low", "medium", "high", "xhigh", "max"];
2
+ export function isAstraReasoning(value) {
3
+ return ASTRA_REASONING_LEVELS.includes(value);
4
+ }
5
+ export function getAstraReasoning() {
6
+ const value = process.env.CODEX_REASONING_EFFORT ?? "medium";
7
+ return isAstraReasoning(value) ? value : "medium";
8
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "appback-remoteagent",
3
- "version": "0.23.2",
3
+ "version": "0.23.3",
4
4
  "description": "Personal installable session server for continuing local AI work across PC and Telegram",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -35,6 +35,10 @@ await fs.chmod(fakeCodex, 0o755);
35
35
  const { CodexAdapter } = await import(path.join(root, "dist", "adapters", "codex-adapter.js"));
36
36
  const adapter = new CodexAdapter(fakeCodex, 5000, "read-only");
37
37
  for (const method of ["buildExecArgs", "buildResumeArgs"]) {
38
+ for (const reasoningEffort of ["low", "medium", "high", "xhigh", "max"]) {
39
+ const args = adapter[method]({model: "gpt-6-astra", reasoningEffort, cwd: tmp, sessionId: "stream-thread"}, path.join(tmp, "output"), "read-only");
40
+ if (!args.includes(`model_reasoning_effort="${reasoningEffort}"`)) throw new Error(`Missing ${reasoningEffort} in ${method}`);
41
+ }
38
42
  const args = adapter[method]({model: "gpt-6-astra", cwd: tmp, sessionId: "stream-thread"}, path.join(tmp, "output"), "read-only");
39
43
  if (args[args.indexOf("-m") + 1] !== "gpt-6-astra" || !args.some((arg, i) => arg === "-c" && args[i + 1] === 'model_reasoning_effort="medium"')) {
40
44
  throw new Error(`Astra medium missing from ${method}`);
@@ -63,6 +63,9 @@ try {
63
63
  );
64
64
 
65
65
  let bridge = createBridge();
66
+ process.env.CODEX_REASONING_EFFORT = "max";
67
+ assert.match(bridge.formatResponses([{provider: "codex", model: primaryModel, reasoningEffort: "high", publicSessionId: "S081", output: "done"}])[0], /\| high \| S081/);
68
+ process.env.CODEX_REASONING_EFFORT = "medium";
66
69
  assert.equal(bridge.formatResponses([{provider: "codex", model: primaryModel, publicSessionId: "S081", output: "done"}])[0], "[CODEX | gpt-6-astra | medium | S081]\ndone");
67
70
  const started = await bridge.startSession("test-bot", "test-chat", "codex");
68
71
  const originalSessionId = started.session.sessionId;
@@ -323,6 +323,13 @@ await send("/start codex");
323
323
  await send("/option retry 6");
324
324
  await send("/option timeout 600");
325
325
  await send("/option intent 4");
326
+ await send("/option reasoning high");
327
+ if (process.env.CODEX_REASONING_EFFORT !== "high" || !(await fs.readFile(path.join(dataDir, ".env"), "utf8")).includes("CODEX_REASONING_EFFORT=high")) {
328
+ throw new Error("Reasoning option was not applied and persisted");
329
+ }
330
+ await send("/option reasoning invalid");
331
+ if (process.env.CODEX_REASONING_EFFORT !== "high") throw new Error("Invalid reasoning changed runtime setting");
332
+ await send("/option reasoning medium");
326
333
  await send("/secret set REMOTEAGENT_TRANSFER_PASSPHRASE correct-horse-battery-staple");
327
334
  await send("/secret set API_TOKEN telegram-secret-export-value");
328
335
  await send("/secret export REMOTEAGENT_TRANSFER_PASSPHRASE API_TOKEN");