@gh-symphony/cli 0.0.19 → 0.0.20

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.
@@ -6,7 +6,7 @@ import {
6
6
  } from "./chunk-RN2PACNV.js";
7
7
  import {
8
8
  start_default
9
- } from "./chunk-GKENCODJ.js";
9
+ } from "./chunk-EKKT5USP.js";
10
10
  import {
11
11
  GhAuthError,
12
12
  GitHubScopeError,
@@ -5,7 +5,7 @@ import {
5
5
  createStore,
6
6
  releaseProjectLock,
7
7
  resolveOrchestratorLogLevel
8
- } from "./chunk-6CI3UUMH.js";
8
+ } from "./chunk-HZVDTAPS.js";
9
9
  import {
10
10
  deriveIssueWorkspaceKeyFromIdentifier,
11
11
  isFileMissing,
@@ -1384,8 +1384,6 @@ var DEFAULT_POLL_INTERVAL_MS = 3e4;
1384
1384
  var DEFAULT_CONCURRENCY = 3;
1385
1385
  var DEFAULT_RETRY_BACKOFF_MS = 3e4;
1386
1386
  var CONTINUATION_RETRY_DELAY_MS = 1e3;
1387
- var DEFAULT_GLOBAL_MAX_TURNS = 100;
1388
- var DEFAULT_MAX_TOKENS = 256e3;
1389
1387
  var DEFAULT_WORKER_COMMAND = "node packages/worker/dist/index.js";
1390
1388
  var DEFAULT_MAX_NONPRODUCTIVE_TURNS = 3;
1391
1389
  var LOW_RATE_LIMIT_WARNING_THRESHOLD = 0.05;
@@ -1650,9 +1648,6 @@ var OrchestratorService = class {
1650
1648
  if (await this.isFailureRetrySuppressedIssue(tenant, issue, issueRecords, latestRunsByIssueId.get(issue.id) ?? null)) {
1651
1649
  continue;
1652
1650
  }
1653
- if (isIssueBudgetExceeded(resolveIssueBudgetSnapshot(projectRunsAfterReconcile, issue.id), now)) {
1654
- continue;
1655
- }
1656
1651
  const stateLimit = maxConcurrentByState[issue.state];
1657
1652
  if (stateLimit !== void 0) {
1658
1653
  const activeInState = activeByState.get(issue.state) ?? 0;
@@ -2014,7 +2009,7 @@ var OrchestratorService = class {
2014
2009
  const resolution = await loadRepositoryWorkflow(repositoryDirectory, repository);
2015
2010
  return this.resolveWorkflowResolution(repository, cacheRoot, resolution, changed);
2016
2011
  }
2017
- async startRun(tenant, issue, resumeContext) {
2012
+ async startRun(tenant, issue) {
2018
2013
  if (this.shuttingDown || !this.running) {
2019
2014
  throw new Error("Orchestrator is shutting down and cannot start new runs.");
2020
2015
  }
@@ -2076,8 +2071,6 @@ var OrchestratorService = class {
2076
2071
  if (!isUsableWorkflowResolution(workflow)) {
2077
2072
  throw new Error(workflow.validationError ?? "Invalid repository WORKFLOW.md");
2078
2073
  }
2079
- const allProjectRuns = (await this.store.loadAllRuns()).filter((run) => run.projectId === tenant.projectId);
2080
- const issueBudgetSnapshot = resolveIssueBudgetSnapshot(allProjectRuns, issue.id);
2081
2074
  const promptVariables = buildPromptVariables(issue, {
2082
2075
  attempt: null
2083
2076
  // first execution
@@ -2147,17 +2140,19 @@ var OrchestratorService = class {
2147
2140
  SYMPHONY_THREAD_SANDBOX: workflow.workflow.codex.threadSandbox ?? "",
2148
2141
  SYMPHONY_TURN_SANDBOX_POLICY: workflow.workflow.codex.turnSandboxPolicy ?? "",
2149
2142
  SYMPHONY_MAX_TURNS: String(workflow.workflow.agent.maxTurns),
2150
- SYMPHONY_GLOBAL_MAX_TURNS: process.env.SYMPHONY_GLOBAL_MAX_TURNS ?? "",
2151
- SYMPHONY_MAX_TOKENS: process.env.SYMPHONY_MAX_TOKENS ?? "",
2152
2143
  SYMPHONY_MAX_NONPRODUCTIVE_TURNS: process.env.SYMPHONY_MAX_NONPRODUCTIVE_TURNS ?? String(DEFAULT_MAX_NONPRODUCTIVE_TURNS),
2153
- SYMPHONY_SESSION_TIMEOUT_MS: process.env.SYMPHONY_SESSION_TIMEOUT_MS ?? "",
2154
- SYMPHONY_RESUME_THREAD_ID: resumeContext?.threadId ?? "",
2155
- SYMPHONY_CUMULATIVE_TURN_COUNT: String(Math.max(0, resumeContext?.cumulativeTurnCount ?? issueBudgetSnapshot.cumulativeTurnCount)),
2156
- SYMPHONY_CUMULATIVE_INPUT_TOKENS: String(issueBudgetSnapshot.tokenUsage.inputTokens),
2157
- SYMPHONY_CUMULATIVE_OUTPUT_TOKENS: String(issueBudgetSnapshot.tokenUsage.outputTokens),
2158
- SYMPHONY_CUMULATIVE_TOTAL_TOKENS: String(issueBudgetSnapshot.tokenUsage.totalTokens),
2159
- SYMPHONY_LAST_TURN_SUMMARY: resumeContext?.lastTurnSummary ?? "",
2160
- SYMPHONY_SESSION_STARTED_AT: issueBudgetSnapshot.sessionStartedAt ?? "",
2144
+ // Clear legacy resume/budget env so fresh worker sessions do not
2145
+ // inherit stale process-level values.
2146
+ SYMPHONY_GLOBAL_MAX_TURNS: "",
2147
+ SYMPHONY_MAX_TOKENS: "",
2148
+ SYMPHONY_SESSION_TIMEOUT_MS: "",
2149
+ SYMPHONY_RESUME_THREAD_ID: "",
2150
+ SYMPHONY_CUMULATIVE_TURN_COUNT: "0",
2151
+ SYMPHONY_CUMULATIVE_INPUT_TOKENS: "0",
2152
+ SYMPHONY_CUMULATIVE_OUTPUT_TOKENS: "0",
2153
+ SYMPHONY_CUMULATIVE_TOTAL_TOKENS: "0",
2154
+ SYMPHONY_LAST_TURN_SUMMARY: "",
2155
+ SYMPHONY_SESSION_STARTED_AT: "",
2161
2156
  SYMPHONY_READ_TIMEOUT_MS: String(workflow.workflow.codex.readTimeoutMs),
2162
2157
  SYMPHONY_TURN_TIMEOUT_MS: String(workflow.workflow.codex.turnTimeoutMs)
2163
2158
  }),
@@ -2402,17 +2397,17 @@ var OrchestratorService = class {
2402
2397
  }
2403
2398
  return this.restartRun(tenant, run, issueRecords, now, workerSessionId);
2404
2399
  }
2405
- if (workerInfo.exitClassification === "budget-exceeded" || workerInfo.exitClassification === "convergence-detected") {
2400
+ if (workerInfo.exitClassification === "convergence-detected") {
2406
2401
  const completedRun = {
2407
2402
  ...runWithTokens,
2408
- status: workerInfo.exitClassification === "budget-exceeded" ? "succeeded" : "failed",
2403
+ status: "failed",
2409
2404
  processId: null,
2410
2405
  updatedAt: now.toISOString(),
2411
2406
  completedAt: now.toISOString(),
2412
2407
  nextRetryAt: null,
2413
2408
  retryKind: null,
2414
- lastError: workerInfo.exitClassification === "budget-exceeded" ? null : runWithTokens.lastError,
2415
- runPhase: runWithTokens.runPhase ?? (workerInfo.exitClassification === "budget-exceeded" ? "succeeded" : "failed")
2409
+ lastError: runWithTokens.lastError,
2410
+ runPhase: runWithTokens.runPhase ?? "failed"
2416
2411
  };
2417
2412
  await this.store.saveRun(completedRun);
2418
2413
  this.logVerbose(`[run-completed] ${completedRun.runId} status=${completedRun.status}`);
@@ -2749,9 +2744,6 @@ var OrchestratorService = class {
2749
2744
  }
2750
2745
  async resolveRetryRestartAction(tenant, run, trackerDependencies = {}) {
2751
2746
  try {
2752
- if (isIssueBudgetExceeded(resolveIssueBudgetSnapshot((await this.store.loadAllRuns()).filter((candidate) => candidate.projectId === tenant.projectId), run.issueId), this.now())) {
2753
- return "release";
2754
- }
2755
2747
  const eligibleContext = await this.fetchTrackedIssueEligibilityContext(tenant, run.issueIdentifier, trackerDependencies);
2756
2748
  if (!eligibleContext) {
2757
2749
  return "release";
@@ -2863,18 +2855,14 @@ var OrchestratorService = class {
2863
2855
  };
2864
2856
  await this.store.saveRun(supersededRecord);
2865
2857
  const issue = resolveTrackerAdapter2(tenant.tracker).reviveIssue(tenant, run);
2866
- const restarted = await this.startRun(tenant, issue, {
2867
- threadId: run.threadId ?? run.runtimeSession?.threadId ?? null,
2868
- cumulativeTurnCount: resolvePersistedCumulativeTurnCount(run),
2869
- lastTurnSummary: run.lastTurnSummary ?? null
2870
- });
2858
+ const restarted = await this.startRun(tenant, issue);
2871
2859
  const recoveredRecord = {
2872
2860
  ...restarted,
2873
2861
  attempt: run.attempt,
2874
2862
  retryKind: run.retryKind ?? "recovery",
2875
2863
  createdAt: run.createdAt,
2876
2864
  issueWorkspaceKey: run.issueWorkspaceKey,
2877
- threadId: run.threadId ?? run.runtimeSession?.threadId ?? null,
2865
+ threadId: null,
2878
2866
  cumulativeTurnCount: resolvePersistedCumulativeTurnCount(run),
2879
2867
  lastTurnSummary: run.lastTurnSummary ?? null,
2880
2868
  turnCount: 0
@@ -3279,45 +3267,6 @@ function hasConvergenceLockedRun(runs, issueId, issueState, issueUpdatedAt) {
3279
3267
  }
3280
3268
  return issueUpdatedAtMs <= convergedAtMs;
3281
3269
  }
3282
- function resolveIssueBudgetSnapshot(runs, issueId) {
3283
- const issueRuns = runs.filter((run) => run.issueId === issueId);
3284
- const startedAtCandidates = issueRuns.map((run) => run.startedAt).filter((value) => typeof value === "string");
3285
- return {
3286
- cumulativeTurnCount: issueRuns.reduce((total, run) => total + resolvePersistedCumulativeTurnCount(run), 0),
3287
- tokenUsage: issueRuns.reduce((total, run) => ({
3288
- inputTokens: total.inputTokens + (run.tokenUsage?.inputTokens ?? 0),
3289
- outputTokens: total.outputTokens + (run.tokenUsage?.outputTokens ?? 0),
3290
- totalTokens: total.totalTokens + (run.tokenUsage?.totalTokens ?? 0)
3291
- }), {
3292
- inputTokens: 0,
3293
- outputTokens: 0,
3294
- totalTokens: 0
3295
- }),
3296
- sessionStartedAt: startedAtCandidates.sort((left, right) => left.localeCompare(right))[0] ?? null
3297
- };
3298
- }
3299
- function isIssueBudgetExceeded(snapshot, now, env = process.env) {
3300
- const globalMaxTurns = parsePositiveInteger(env.SYMPHONY_GLOBAL_MAX_TURNS ?? "") ?? DEFAULT_GLOBAL_MAX_TURNS;
3301
- if (snapshot.cumulativeTurnCount >= globalMaxTurns) {
3302
- return true;
3303
- }
3304
- const maxTokens = parsePositiveInteger(env.SYMPHONY_MAX_TOKENS ?? "") ?? DEFAULT_MAX_TOKENS;
3305
- if (snapshot.tokenUsage.totalTokens >= maxTokens) {
3306
- return true;
3307
- }
3308
- const sessionTimeoutMs = parsePositiveInteger(env.SYMPHONY_SESSION_TIMEOUT_MS ?? "");
3309
- if (sessionTimeoutMs === null || snapshot.sessionStartedAt === null) {
3310
- return false;
3311
- }
3312
- return now.getTime() - new Date(snapshot.sessionStartedAt).getTime() >= sessionTimeoutMs;
3313
- }
3314
- function parsePositiveInteger(value) {
3315
- const parsed = Number(value);
3316
- if (!Number.isFinite(parsed) || parsed <= 0) {
3317
- return null;
3318
- }
3319
- return Math.floor(parsed);
3320
- }
3321
3270
  function resolveCumulativeTurnCount(run, turnCount) {
3322
3271
  const carriedTotal = resolvePersistedCumulativeTurnCount(run);
3323
3272
  if (turnCount === null) {
package/dist/index.js CHANGED
@@ -280,19 +280,19 @@ ${bashFunction}complete -F _gh_symphony_completion gh-symphony
280
280
  var COMMANDS = {
281
281
  workflow: () => import("./workflow-TBIFY5MO.js"),
282
282
  init: () => import("./init-KZT6YNOH.js"),
283
- setup: () => import("./setup-K4CYYJBF.js"),
283
+ setup: () => import("./setup-VWB7RZUQ.js"),
284
284
  doctor: () => import("./doctor-IYHCFXOZ.js"),
285
- upgrade: () => import("./upgrade-F4VE4XBS.js"),
286
- start: () => import("./start-M6IQGRFO.js"),
285
+ upgrade: () => import("./upgrade-3YNF3VKY.js"),
286
+ start: () => import("./start-ENFLZUI6.js"),
287
287
  stop: () => import("./stop-7MFCBQVW.js"),
288
288
  status: () => import("./status-QSCFVGRQ.js"),
289
- run: () => import("./run-XI2S5Y4V.js"),
290
- recover: () => import("./recover-C3V2QAUB.js"),
289
+ run: () => import("./run-ETC5UTRA.js"),
290
+ recover: () => import("./recover-5KQI7WH5.js"),
291
291
  logs: () => import("./logs-6JKKYDGJ.js"),
292
- project: () => import("./project-DNALEWO3.js"),
292
+ project: () => import("./project-UUVHS3ZR.js"),
293
293
  repo: () => import("./repo-HDDE7OUI.js"),
294
294
  config: () => import("./config-cmd-DNXNL26Z.js"),
295
- version: () => import("./version-Y5RYNWMF.js")
295
+ version: () => import("./version-NUBTTOG7.js")
296
296
  };
297
297
  function addGlobalOptions(command) {
298
298
  return command.option("--config <dir>", "Config directory").addOption(new Option("--config-dir <dir>").hideHelp()).option("-v, --verbose", "Enable verbose output").option("--json", "Output in JSON format").option("--no-color", "Disable color output");
@@ -3,10 +3,10 @@ import {
3
3
  project_default,
4
4
  promptProjectRegistrationOptions,
5
5
  renderProjectRegistrationSummary
6
- } from "./chunk-H2YXSYOZ.js";
6
+ } from "./chunk-3AWF54PI.js";
7
7
  import "./chunk-RN2PACNV.js";
8
- import "./chunk-GKENCODJ.js";
9
- import "./chunk-6CI3UUMH.js";
8
+ import "./chunk-EKKT5USP.js";
9
+ import "./chunk-HZVDTAPS.js";
10
10
  import "./chunk-M3IFVLQS.js";
11
11
  import "./chunk-TILHWBP6.js";
12
12
  import "./chunk-XN5ABWZ6.js";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runCli
4
- } from "./chunk-6CI3UUMH.js";
4
+ } from "./chunk-HZVDTAPS.js";
5
5
  import "./chunk-M3IFVLQS.js";
6
6
  import {
7
7
  resolveRuntimeRoot
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runCli
4
- } from "./chunk-6CI3UUMH.js";
4
+ } from "./chunk-HZVDTAPS.js";
5
5
  import "./chunk-M3IFVLQS.js";
6
6
  import {
7
7
  resolveRuntimeRoot
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  promptProjectRegistrationOptions,
4
4
  renderProjectRegistrationSummary
5
- } from "./chunk-H2YXSYOZ.js";
5
+ } from "./chunk-3AWF54PI.js";
6
6
  import {
7
7
  abortIfCancelled,
8
8
  buildAutomaticStateMappings,
@@ -16,8 +16,8 @@ import {
16
16
  writeEcosystem,
17
17
  writeWorkflowPlan
18
18
  } from "./chunk-RN2PACNV.js";
19
- import "./chunk-GKENCODJ.js";
20
- import "./chunk-6CI3UUMH.js";
19
+ import "./chunk-EKKT5USP.js";
20
+ import "./chunk-HZVDTAPS.js";
21
21
  import "./chunk-M3IFVLQS.js";
22
22
  import {
23
23
  GhAuthError,
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  shutdownForegroundOrchestrator,
4
4
  start_default
5
- } from "./chunk-GKENCODJ.js";
6
- import "./chunk-6CI3UUMH.js";
5
+ } from "./chunk-EKKT5USP.js";
6
+ import "./chunk-HZVDTAPS.js";
7
7
  import "./chunk-M3IFVLQS.js";
8
8
  import "./chunk-TILHWBP6.js";
9
9
  import "./chunk-MVRF7BES.js";
@@ -16,8 +16,8 @@ function execFileAsync(file, args, execFileImpl = execFileCallback) {
16
16
  });
17
17
  }
18
18
  function resolveCurrentCliVersion() {
19
- if ("0.0.19".length > 0) {
20
- return "0.0.19";
19
+ if ("0.0.20".length > 0) {
20
+ return "0.0.20";
21
21
  }
22
22
  const pkg = JSON.parse(
23
23
  readFileSync(new URL("../../package.json", import.meta.url), "utf8")
@@ -2,7 +2,7 @@
2
2
 
3
3
  // src/commands/version.ts
4
4
  var handler = async (_args, options) => {
5
- const version = "0.0.19";
5
+ const version = "0.0.20";
6
6
  if (options.json) {
7
7
  process.stdout.write(JSON.stringify({ version }) + "\n");
8
8
  } else {
@@ -92,8 +92,7 @@ function buildCodexRuntimePlan(config) {
92
92
  ...gitCredentialHelper,
93
93
  ...tool.env
94
94
  },
95
- tools: [tool],
96
- resumeThreadId: config.resumeThreadId?.trim() || null
95
+ tools: [tool]
97
96
  };
98
97
  }
99
98
  function launchCodexAppServer(plan, spawnImpl = spawn) {
@@ -196,8 +195,7 @@ function resolveLocalRuntimeLaunchConfig(env = process.env) {
196
195
  agentCredentialCachePath: env.AGENT_CREDENTIAL_CACHE_PATH,
197
196
  githubProjectId: env.GITHUB_PROJECT_ID,
198
197
  githubGraphqlApiUrl: env.GITHUB_GRAPHQL_API_URL,
199
- agentCommand: env.SYMPHONY_AGENT_COMMAND,
200
- resumeThreadId: env.SYMPHONY_RESUME_THREAD_ID
198
+ agentCommand: env.SYMPHONY_AGENT_COMMAND
201
199
  };
202
200
  }
203
201
  async function runLocalRuntimeLauncher(env = process.env) {
@@ -556,111 +554,20 @@ function resolveExitRunPhase(currentRunPhase, exit) {
556
554
  return exit.code === 0 && !exit.signal ? "succeeded" : "failed";
557
555
  }
558
556
 
559
- // ../worker/dist/session-budget.js
560
- function resolveSessionBudgetState(env) {
561
- return {
562
- cumulativeTurnCount: parseNonNegativeInteger(env.SYMPHONY_CUMULATIVE_TURN_COUNT),
563
- tokenUsageBaseline: {
564
- inputTokens: parseNonNegativeInteger(env.SYMPHONY_CUMULATIVE_INPUT_TOKENS),
565
- outputTokens: parseNonNegativeInteger(env.SYMPHONY_CUMULATIVE_OUTPUT_TOKENS),
566
- totalTokens: parseNonNegativeInteger(env.SYMPHONY_CUMULATIVE_TOTAL_TOKENS)
567
- },
568
- sessionStartedAt: normalizeTimestamp(env.SYMPHONY_SESSION_STARTED_AT),
569
- globalMaxTurns: parsePositiveInteger(env.SYMPHONY_GLOBAL_MAX_TURNS),
570
- maxTokens: parsePositiveInteger(env.SYMPHONY_MAX_TOKENS),
571
- sessionTimeoutMs: parsePositiveInteger(env.SYMPHONY_SESSION_TIMEOUT_MS)
572
- };
573
- }
574
- function resolveBudgetExceededReason(budget, currentSessionTurnCount, currentTokenUsage, now) {
575
- const totalTurns = budget.cumulativeTurnCount + currentSessionTurnCount;
576
- if (budget.globalMaxTurns !== null && totalTurns >= budget.globalMaxTurns) {
577
- return "global-turns";
578
- }
579
- const totalTokens = budget.tokenUsageBaseline.totalTokens + currentTokenUsage.totalTokens;
580
- if (budget.maxTokens !== null && totalTokens >= budget.maxTokens) {
581
- return "tokens";
582
- }
583
- if (budget.sessionTimeoutMs !== null && budget.sessionStartedAt !== null && now.getTime() - new Date(budget.sessionStartedAt).getTime() >= budget.sessionTimeoutMs) {
584
- return "session-timeout";
585
- }
586
- return null;
587
- }
588
- function parsePositiveInteger(value) {
589
- if (!value) {
590
- return null;
591
- }
592
- const parsed = Number(value);
593
- if (!Number.isFinite(parsed) || parsed <= 0) {
594
- return null;
595
- }
596
- return Math.floor(parsed);
597
- }
598
- function parseNonNegativeInteger(value) {
599
- if (!value) {
600
- return 0;
601
- }
602
- const parsed = Number(value);
603
- if (!Number.isFinite(parsed) || parsed < 0) {
604
- return 0;
605
- }
606
- return Math.floor(parsed);
607
- }
608
- function normalizeTimestamp(value) {
609
- if (!value) {
610
- return null;
611
- }
612
- const parsed = Date.parse(value);
613
- return Number.isFinite(parsed) ? new Date(parsed).toISOString() : null;
614
- }
615
-
616
557
  // ../worker/dist/thread-resume.js
617
558
  var DEFAULT_CONTINUATION_GUIDANCE = "Continue working on the issue. Review your progress and complete any remaining tasks.";
618
- function parseNonNegativeInteger2(value) {
559
+ function parseNonNegativeInteger(value) {
619
560
  const parsed = typeof value === "number" ? value : Number.parseInt(value ?? "", 10);
620
561
  if (!Number.isFinite(parsed) || parsed <= 0) {
621
562
  return 0;
622
563
  }
623
564
  return Math.floor(parsed);
624
565
  }
625
- function resolveRemainingTurns(maxTurns, cumulativeTurnCount) {
626
- return Math.max(0, parseNonNegativeInteger2(maxTurns) - parseNonNegativeInteger2(cumulativeTurnCount));
627
- }
628
- function buildInitialTurnInput({ renderedPrompt, mode, lastTurnSummary, cumulativeTurnCount = 0, continuationGuidance }) {
629
- if (mode === "fresh") {
630
- return renderedPrompt;
631
- }
632
- const renderedContinuationGuidance = buildContinuationTurnInput({
633
- continuationGuidance,
634
- lastTurnSummary,
635
- cumulativeTurnCount
636
- });
637
- const normalizedSummary = normalizeContinuationVariable(lastTurnSummary) ?? "No previous turn summary was captured.";
638
- const normalizedCumulativeTurnCount = Math.max(0, parseNonNegativeInteger2(cumulativeTurnCount));
639
- if (mode === "resume") {
640
- return [
641
- "Resume work on this issue using the existing thread context.",
642
- `Previous worker turns completed: ${normalizedCumulativeTurnCount}.`,
643
- `Previous session summary: ${normalizedSummary}`,
644
- renderedContinuationGuidance
645
- ].join("\n");
646
- }
647
- return [
648
- "Resume work on this issue from a previous worker session.",
649
- "",
650
- "Original issue instructions:",
651
- renderedPrompt,
652
- "",
653
- "Previous session summary:",
654
- normalizedSummary,
655
- "",
656
- renderedContinuationGuidance
657
- ].join("\n");
658
- }
659
566
  function buildContinuationTurnInput({ continuationGuidance, lastTurnSummary, cumulativeTurnCount = 0 }) {
660
567
  const template = continuationGuidance?.trim() || DEFAULT_CONTINUATION_GUIDANCE;
661
568
  return renderContinuationGuidance(template, {
662
569
  lastTurnSummary: normalizeContinuationVariable(lastTurnSummary) ?? "No previous turn summary was captured.",
663
- cumulativeTurnCount: String(Math.max(0, parseNonNegativeInteger2(cumulativeTurnCount)))
570
+ cumulativeTurnCount: String(Math.max(0, parseNonNegativeInteger(cumulativeTurnCount)))
664
571
  });
665
572
  }
666
573
  function normalizeContinuationVariable(value) {
@@ -693,6 +600,29 @@ function renderContinuationGuidance(template, variables) {
693
600
  return rendered;
694
601
  }
695
602
 
603
+ // ../worker/dist/turn-limits.js
604
+ var DEFAULT_SESSION_MAX_TURNS = 20;
605
+ function resolveMaxTurns(value) {
606
+ const parsed = typeof value === "number" ? value : Number(value);
607
+ if (!Number.isFinite(parsed)) {
608
+ return {
609
+ maxTurns: DEFAULT_SESSION_MAX_TURNS,
610
+ exhaustedBeforeStart: false
611
+ };
612
+ }
613
+ const normalized = Math.trunc(parsed);
614
+ if (normalized <= 0) {
615
+ return {
616
+ maxTurns: 0,
617
+ exhaustedBeforeStart: true
618
+ };
619
+ }
620
+ return {
621
+ maxTurns: normalized,
622
+ exhaustedBeforeStart: false
623
+ };
624
+ }
625
+
696
626
  // ../worker/dist/token-usage.js
697
627
  import { mkdir as mkdir2, writeFile as writeFile3 } from "fs/promises";
698
628
  import { dirname as dirname2, join as join2 } from "path";
@@ -720,7 +650,6 @@ function resolveTokenUsageArtifactPath(env) {
720
650
 
721
651
  // ../worker/dist/index.js
722
652
  var launcherEnv = loadLauncherEnvironment(process.env);
723
- var sessionBudgetState = resolveSessionBudgetState(launcherEnv);
724
653
  var runtimeState = {
725
654
  status: launcherEnv.SYMPHONY_RUN_ID ? "starting" : "idle",
726
655
  executionPhase: null,
@@ -741,9 +670,9 @@ var runtimeState = {
741
670
  lastError: null
742
671
  } : null,
743
672
  tokenUsage: {
744
- inputTokens: sessionBudgetState.tokenUsageBaseline.inputTokens,
745
- outputTokens: sessionBudgetState.tokenUsageBaseline.outputTokens,
746
- totalTokens: sessionBudgetState.tokenUsageBaseline.totalTokens
673
+ inputTokens: 0,
674
+ outputTokens: 0,
675
+ totalTokens: 0
747
676
  },
748
677
  lastEventAt: null,
749
678
  rateLimits: null,
@@ -938,7 +867,7 @@ function resolveTurnTokenUsageDelta(baseline) {
938
867
  };
939
868
  }
940
869
  function resolveSessionTokenUsageDelta() {
941
- return resolveTurnTokenUsageDelta(sessionBudgetState.tokenUsageBaseline);
870
+ return cloneTokenUsageSnapshot();
942
871
  }
943
872
  async function persistSessionTokenUsageArtifact(env) {
944
873
  await persistTokenUsageArtifact(env, resolveSessionTokenUsageDelta());
@@ -1071,17 +1000,13 @@ async function runCodexClientProtocol(child, plan, env, options) {
1071
1000
  process.stderr.write("[worker] codex process has no stdio pipes; cannot run client protocol\n");
1072
1001
  return;
1073
1002
  }
1074
- const maxTurns = Number(env.SYMPHONY_MAX_TURNS) || 20;
1075
- const cumulativeTurnCount = parseNonNegativeInteger2(env.SYMPHONY_CUMULATIVE_TURN_COUNT);
1076
- const remainingTurns = resolveRemainingTurns(maxTurns, cumulativeTurnCount);
1003
+ const { maxTurns, exhaustedBeforeStart } = resolveMaxTurns(env.SYMPHONY_MAX_TURNS);
1077
1004
  const readTimeoutMs = Number(env.SYMPHONY_READ_TIMEOUT_MS) || 5e3;
1078
1005
  const turnTimeoutMs = Number(env.SYMPHONY_TURN_TIMEOUT_MS) || 36e5;
1079
1006
  const maxNonProductiveTurns = resolveMaxNonProductiveTurns(env);
1080
1007
  const issueIdentifier = env.SYMPHONY_ISSUE_IDENTIFIER ?? "";
1081
- const lastTurnSummary = env.SYMPHONY_LAST_TURN_SUMMARY ?? null;
1082
1008
  const continuationGuidance = env.SYMPHONY_CONTINUATION_GUIDANCE ?? options.continuationGuidance;
1083
1009
  const { approvalPolicy, threadSandbox, turnSandboxPolicy } = resolveCodexPolicySettings(env);
1084
- const budgetState = resolveSessionBudgetState(env);
1085
1010
  let previousTurnProgressSnapshot = {
1086
1011
  ...captureTurnWorkspaceSnapshot(plan.cwd),
1087
1012
  lastError: runtimeState.run?.lastError ?? null
@@ -1101,16 +1026,8 @@ async function runCodexClientProtocol(child, plan, env, options) {
1101
1026
  let userInputRequired = false;
1102
1027
  let turnTerminalFailurePhase = null;
1103
1028
  let activeTurnTelemetry = null;
1104
- let budgetExceededReason = null;
1105
1029
  let consecutiveNonProductiveTurns = 0;
1106
1030
  let convergenceDetected = false;
1107
- function checkSessionBudgets(currentSessionTurnCount) {
1108
- return resolveBudgetExceededReason(budgetState, currentSessionTurnCount, {
1109
- inputTokens: runtimeState.tokenUsage.inputTokens - budgetState.tokenUsageBaseline.inputTokens,
1110
- outputTokens: runtimeState.tokenUsage.outputTokens - budgetState.tokenUsageBaseline.outputTokens,
1111
- totalTokens: runtimeState.tokenUsage.totalTokens - budgetState.tokenUsageBaseline.totalTokens
1112
- }, /* @__PURE__ */ new Date());
1113
- }
1114
1031
  function resolvePendingTurnCompletion() {
1115
1032
  if (turnCompletedResolve) {
1116
1033
  turnCompletedResolve();
@@ -1428,27 +1345,6 @@ async function runCodexClientProtocol(child, plan, env, options) {
1428
1345
  env: t.env
1429
1346
  };
1430
1347
  }
1431
- if (remainingTurns <= 0) {
1432
- process.stderr.write(`[worker] max_turns already exhausted by previous sessions (${cumulativeTurnCount}/${maxTurns})
1433
- `);
1434
- runtimeState.status = "completed";
1435
- runtimeState.runPhase = "succeeded";
1436
- runtimeState.sessionInfo.exitClassification = classifySessionExit({
1437
- runPhase: runtimeState.runPhase,
1438
- userInputRequired: false,
1439
- budgetExceeded: false,
1440
- convergenceDetected: false,
1441
- maxTurnsReached: true
1442
- });
1443
- stopOrchestratorHeartbeatTimer();
1444
- emitOrchestratorHeartbeat();
1445
- await persistSessionTokenUsageArtifact(env);
1446
- await waitForPendingOrchestratorChannelFlush(resolveTerminalOrchestratorChannelFlushTimeoutMs());
1447
- setTimeout(() => {
1448
- process.exit(0);
1449
- }, 1500);
1450
- return;
1451
- }
1452
1348
  const baseThreadParams = {
1453
1349
  cwd: plan.cwd,
1454
1350
  developerInstructions: renderedPrompt,
@@ -1458,36 +1354,12 @@ async function runCodexClientProtocol(child, plan, env, options) {
1458
1354
  mcp_servers: mcpServers
1459
1355
  }
1460
1356
  };
1461
- const resumeThreadId = plan.resumeThreadId;
1462
- let threadBootstrapMode = "fresh";
1463
1357
  process.stderr.write(`[worker] starting codex thread (mcp_servers: ${Object.keys(mcpServers).join(", ")})
1464
1358
  `);
1465
- let threadResult;
1466
- if (resumeThreadId) {
1467
- process.stderr.write(`[worker] attempting thread/resume for ${resumeThreadId}
1468
- `);
1469
- try {
1470
- threadResult = await sendRequestWithTimeout("thread-resume-1", "thread/resume", {
1471
- ...baseThreadParams,
1472
- threadId: resumeThreadId
1473
- });
1474
- threadBootstrapMode = "resume";
1475
- } catch (error) {
1476
- const message = error instanceof Error ? error.message : String(error ?? "unknown");
1477
- threadBootstrapMode = "soft-resume";
1478
- process.stderr.write(`[worker] thread/resume failed for ${resumeThreadId}: ${message}; falling back to thread/start
1479
- `);
1480
- threadResult = await sendRequestWithTimeout("thread-1", "thread/start", {
1481
- ...baseThreadParams,
1482
- ephemeral: false
1483
- });
1484
- }
1485
- } else {
1486
- threadResult = await sendRequestWithTimeout("thread-1", "thread/start", {
1487
- ...baseThreadParams,
1488
- ephemeral: false
1489
- });
1490
- }
1359
+ const threadResult = await sendRequestWithTimeout("thread-1", "thread/start", {
1360
+ ...baseThreadParams,
1361
+ ephemeral: false
1362
+ });
1491
1363
  const threadId = threadResult.thread_id ?? threadResult.thread?.id;
1492
1364
  runtimeState.sessionInfo.threadId = threadId ?? null;
1493
1365
  runtimeState.sessionInfo.turnId = null;
@@ -1502,31 +1374,21 @@ async function runCodexClientProtocol(child, plan, env, options) {
1502
1374
  }
1503
1375
  let turnCount = 0;
1504
1376
  let requestIdCounter = 0;
1505
- let maxTurnsReached = false;
1506
- for (let turn = 0; turn < remainingTurns; turn++) {
1507
- budgetExceededReason = checkSessionBudgets(turn);
1508
- if (budgetExceededReason) {
1509
- process.stderr.write(`[worker] session budget exceeded (${budgetExceededReason}) \u2014 exiting
1377
+ let maxTurnsReached = exhaustedBeforeStart;
1378
+ if (exhaustedBeforeStart) {
1379
+ process.stderr.write(`[worker] max_turns (${String(env.SYMPHONY_MAX_TURNS ?? maxTurns)}) does not allow any turns for this worker session \u2014 exiting
1510
1380
  `);
1511
- break;
1512
- }
1381
+ }
1382
+ for (let turn = 0; turn < maxTurns; turn++) {
1513
1383
  turnCount = turn + 1;
1514
- const globalTurnCount = cumulativeTurnCount + turnCount;
1515
1384
  runtimeState.sessionInfo.turnCount = turnCount;
1516
1385
  runtimeState.runPhase = "streaming_turn";
1517
1386
  const isFirstTurn = turn === 0;
1518
- const turnInput = isFirstTurn ? buildInitialTurnInput({
1519
- renderedPrompt,
1520
- mode: threadBootstrapMode,
1521
- lastTurnSummary,
1522
- cumulativeTurnCount,
1523
- continuationGuidance
1524
- }) : buildContinuationTurnInput({
1387
+ const turnInput = isFirstTurn ? renderedPrompt : buildContinuationTurnInput({
1525
1388
  continuationGuidance,
1526
- lastTurnSummary,
1527
- cumulativeTurnCount: globalTurnCount - 1
1389
+ cumulativeTurnCount: turn
1528
1390
  });
1529
- process.stderr.write(`[worker] starting codex turn ${globalTurnCount}/${maxTurns}${isFirstTurn ? " (initial)" : " (continuation)"}
1391
+ process.stderr.write(`[worker] starting codex turn ${turnCount}/${maxTurns}${isFirstTurn ? " (initial)" : " (continuation)"}
1530
1392
  `);
1531
1393
  requestIdCounter += 1;
1532
1394
  const turnRequestId = `turn-${requestIdCounter}`;
@@ -1566,15 +1428,9 @@ async function runCodexClientProtocol(child, plan, env, options) {
1566
1428
  `);
1567
1429
  break;
1568
1430
  }
1569
- budgetExceededReason = checkSessionBudgets(turnCount);
1570
- if (budgetExceededReason) {
1571
- process.stderr.write(`[worker] session budget exceeded (${budgetExceededReason}) \u2014 exiting
1572
- `);
1573
- break;
1574
- }
1575
- if (turn + 1 >= remainingTurns) {
1431
+ if (turn + 1 >= maxTurns) {
1576
1432
  maxTurnsReached = true;
1577
- process.stderr.write(`[worker] max_turns (${maxTurns}) reached across sessions \u2014 exiting
1433
+ process.stderr.write(`[worker] max_turns (${maxTurns}) reached for this worker session \u2014 exiting
1578
1434
  `);
1579
1435
  break;
1580
1436
  }
@@ -1622,7 +1478,7 @@ async function runCodexClientProtocol(child, plan, env, options) {
1622
1478
  runtimeState.sessionInfo.exitClassification = classifySessionExit({
1623
1479
  runPhase: runtimeState.runPhase,
1624
1480
  userInputRequired,
1625
- budgetExceeded: budgetExceededReason !== null,
1481
+ budgetExceeded: false,
1626
1482
  convergenceDetected,
1627
1483
  maxTurnsReached
1628
1484
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gh-symphony/cli",
3
- "version": "0.0.19",
3
+ "version": "0.0.20",
4
4
  "license": "MIT",
5
5
  "author": "hojinzs",
6
6
  "description": "Interactive CLI for GitHub Symphony orchestration",
@@ -44,8 +44,8 @@
44
44
  "@gh-symphony/core": "0.0.14",
45
45
  "@gh-symphony/orchestrator": "0.0.14",
46
46
  "@gh-symphony/dashboard": "0.0.14",
47
- "@gh-symphony/worker": "0.0.14",
48
- "@gh-symphony/tracker-github": "0.0.14"
47
+ "@gh-symphony/tracker-github": "0.0.14",
48
+ "@gh-symphony/worker": "0.0.14"
49
49
  },
50
50
  "scripts": {
51
51
  "build": "tsup",