vibe64 0.1.26 → 0.1.28

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.
Files changed (111) hide show
  1. package/dist/assets/{VRadioGroup-D6WRdUau.js → VRadioGroup-BuRMvs7d.js} +1 -1
  2. package/dist/assets/{VTabs-BGX5dGoS.js → VTabs-CQq9CAuT.js} +1 -1
  3. package/dist/assets/{VTextarea-CD6H_mtJ.js → VTextarea-Duvzvmjq.js} +1 -1
  4. package/dist/assets/{Vibe64AsyncModuleState-Cel3OePx.js → Vibe64AsyncModuleState-DuuzlJDp.js} +1 -1
  5. package/dist/assets/{Vibe64AuthSettingsButton-CMMeacsl.js → Vibe64AuthSettingsButton-CKOCL-kl.js} +1 -1
  6. package/dist/assets/{Vibe64DashboardShell-C2UhX2ke.js → Vibe64DashboardShell-DMT8aOp1.js} +1 -1
  7. package/dist/assets/{Vibe64DatabaseWorkspace-C43TE5-O.js → Vibe64DatabaseWorkspace-CqH0zfFZ.js} +1 -1
  8. package/dist/assets/{Vibe64OutputControls-BmzH2ASf.js → Vibe64OutputControls-DDILWOYb.js} +1 -1
  9. package/dist/assets/{Vibe64RepositoryWorkspace-CT9tkvHh.js → Vibe64RepositoryWorkspace-D11BZHtF.js} +1 -1
  10. package/dist/assets/Vibe64SessionFiles-BSoB7Xrs.js +1 -0
  11. package/dist/assets/{Vibe64SessionFiles-CH5PCxwg.css → Vibe64SessionFiles-DVux69YS.css} +1 -1
  12. package/dist/assets/{Vibe64SubsystemsView-DNx-pZp8.js → Vibe64SubsystemsView-tLlxmK3M.js} +1 -1
  13. package/dist/assets/{Vibe64SystemWorldView-UdNJZbqX.js → Vibe64SystemWorldView-8og8TPyL.js} +1 -1
  14. package/dist/assets/{_sessionId_-DZTmI19N.js → _sessionId_-Dts6gv4W.js} +1 -1
  15. package/dist/assets/{_sessionTool_-CugJWIK4.js → _sessionTool_-bHJUlHCW.js} +1 -1
  16. package/dist/assets/_slug_-B3fqw8Sl.js +12 -0
  17. package/dist/assets/_slug_-DfHqx2_b.css +1 -0
  18. package/dist/assets/{dashboard-D4NXqPY1.js → dashboard-CcD6GIaB.js} +1 -1
  19. package/dist/assets/{definitions-CyUCoqu-.js → definitions-ChGXm26a.js} +1 -1
  20. package/dist/assets/{error-kYKTA8fu.js → error-CStS-Kbt.js} +1 -1
  21. package/dist/assets/{index-hy7AAm-B.js → index-B262pg9q.js} +1 -1
  22. package/dist/assets/{index-DX6Rm6jZ.js → index-B_grpOta.js} +1 -1
  23. package/dist/assets/{index-Cpt19GIW.js → index-BtHA_DX9.js} +1 -1
  24. package/dist/assets/{index-SBc6H2In.js → index-C4KnwA0g.js} +4 -4
  25. package/dist/assets/{index-DeRx59Iq.js → index-CC7BFJAg.js} +1 -1
  26. package/dist/assets/{index-6Kv-7k0S.js → index-CKFrWxnr.js} +1 -1
  27. package/dist/assets/{index-BDe3NnFP.js → index-CPG-_bMW.js} +1 -1
  28. package/dist/assets/{index-CuEZOpKL.js → index-CV9pOpbQ.js} +1 -1
  29. package/dist/assets/{index-D4p1hWbM.js → index-DIQxSy1c.js} +1 -1
  30. package/dist/assets/{index-D5vnCntG.js → index-DVaJ8fEr.js} +1 -1
  31. package/dist/assets/{index-BGu7hfTb.js → index-DtGqjUin.js} +1 -1
  32. package/dist/assets/{index-Cca4WXur.js → index-YQALXwL4.js} +1 -1
  33. package/dist/assets/{placement-Nw6LF6xp.js → placement-Cyt3SnRe.js} +1 -1
  34. package/dist/assets/{studioGateApi-B2eiydiA.js → studioGateApi-BwBnOYBp.js} +1 -1
  35. package/dist/assets/{useArchivedVibe64Sessions-LeGda1Y9.js → useArchivedVibe64Sessions-_FKt1-qN.js} +1 -1
  36. package/dist/assets/{useVibe64ConversationLog-BPyKYbvH.js → useVibe64ConversationLog-BjnpqM9K.js} +1 -1
  37. package/dist/assets/{useVibe64ProjectScope-n5T5l2pY.js → useVibe64ProjectScope-DcDkjbLT.js} +1 -1
  38. package/dist/assets/{useVibe64SessionData-BToODuTg.js → useVibe64SessionData-BHLSmuPm.js} +1 -1
  39. package/dist/assets/{useVibe64SessionSelection-D1wXIWTm.js → useVibe64SessionSelection-G6KQKJxb.js} +1 -1
  40. package/dist/assets/{vibe64RepositoryRealtime-Xo7aIksE.js → vibe64RepositoryRealtime-C2WPn5do.js} +1 -1
  41. package/dist/assets/{vibe64SessionInfo-Dy_4bL3a.js → vibe64SessionInfo-8kuxgD5r.js} +1 -1
  42. package/dist/assets/{vibe64SessionPanelModel-5UED-6V-.js → vibe64SessionPanelModel-DDaXjAJ_.js} +1 -1
  43. package/dist/assets/{vibe64SessionRequestConfig-B7Llggdb.js → vibe64SessionRequestConfig-uGWcyQY6.js} +1 -1
  44. package/dist/assets/{vibe64SessionToolDefinitions-C4maKOfp.js → vibe64SessionToolDefinitions-D5CgOIqa.js} +1 -1
  45. package/dist/assets/{vueRefOrGetterValue-_KqAOFjr.js → vueRefOrGetterValue-CV9c3oBd.js} +1 -1
  46. package/dist/index.html +1 -1
  47. package/node_modules/@jskit-ai/connector-google-calendar/package.json +3 -3
  48. package/node_modules/@jskit-ai/connector-google-calendar/patterns/calendar-cli/example/package.json +4 -4
  49. package/node_modules/@jskit-ai/connectors-catalog/package.json +3 -3
  50. package/node_modules/@jskit-ai/connectors-catalog/test/dbt-semantic-layer.test.js +3 -0
  51. package/node_modules/@jskit-ai/connectors-catalog/test/google-ads.test.js +3 -0
  52. package/node_modules/@jskit-ai/connectors-catalog/test/salesforce.test.js +3 -0
  53. package/node_modules/@jskit-ai/connectors-catalog/test/xero.test.js +3 -0
  54. package/node_modules/@jskit-ai/connectors-core/package.json +4 -4
  55. package/node_modules/@jskit-ai/http-web/package.json +6 -6
  56. package/node_modules/@jskit-ai/kernel/package.json +1 -1
  57. package/node_modules/@jskit-ai/kernel/server/runtime/fastifyBootstrap.js +50 -10
  58. package/node_modules/@jskit-ai/kernel/server/runtime/fastifyBootstrap.test.js +38 -0
  59. package/node_modules/@jskit-ai/kernel/shared/support/normalize.js +8 -0
  60. package/node_modules/@jskit-ai/kernel/shared/support/normalize.test.js +10 -0
  61. package/node_modules/@jskit-ai/payments-core/package.json +1 -1
  62. package/node_modules/@jskit-ai/realtime/package.json +3 -3
  63. package/node_modules/@jskit-ai/realtime/patterns/realtime-application/example/package.json +2 -2
  64. package/node_modules/@jskit-ai/resource-core/package.json +2 -2
  65. package/node_modules/@jskit-ai/resource-crud-core/package.json +3 -3
  66. package/node_modules/@jskit-ai/shell-web/package.json +2 -2
  67. package/node_modules/@jskit-ai/uploads-runtime/package.json +2 -2
  68. package/node_modules/@local/current-app/package.json +1 -1
  69. package/node_modules/@local/main/package.json +2 -2
  70. package/node_modules/@local/studio-health/package.json +1 -1
  71. package/node_modules/@local/vibe64-accounts/package.json +3 -3
  72. package/node_modules/@local/vibe64-core/package.json +1 -1
  73. package/node_modules/@local/vibe64-database-tools/package.json +3 -3
  74. package/node_modules/@local/vibe64-project/package.json +1 -1
  75. package/node_modules/@local/vibe64-runtime/src/server/codexAppServerProvider.js +34 -0
  76. package/node_modules/@local/vibe64-sessions/package.json +1 -1
  77. package/node_modules/@local/vibe64-sessions/src/server/sessionRenewal.js +4 -1
  78. package/node_modules/@local/vibe64-source-editor/package.json +5 -5
  79. package/node_modules/@local/vibe64-system-graph/package.json +3 -3
  80. package/node_modules/@local/vibe64-terminals/package.json +2 -2
  81. package/node_modules/@local/vibe64-terminals/src/server/agent/providers/codexSessionAgentProvider.js +6 -0
  82. package/node_modules/@local/vibe64-terminals/src/server/agent/sessionAgentManager.js +16 -0
  83. package/node_modules/@local/vibe64-terminals/src/server/codexTerminal.js +180 -64
  84. package/node_modules/@local/vibe64-terminals/src/server/registerRoutes.js +12 -0
  85. package/node_modules/@local/vibe64-terminals/src/server/service.js +57 -16
  86. package/node_modules/@local/vibe64-terminals/src/server/sessionPromptHints.js +9 -1
  87. package/node_modules/@local/vibe64-terminals/src/server/sessionSaveCommitMessage.js +1 -1
  88. package/package.json +15 -15
  89. package/packages/current-app/package.json +1 -1
  90. package/packages/main/package.json +2 -2
  91. package/packages/studio-health/package.json +1 -1
  92. package/packages/vibe64-accounts/package.json +3 -3
  93. package/packages/vibe64-core/package.json +1 -1
  94. package/packages/vibe64-database-tools/package.json +3 -3
  95. package/packages/vibe64-project/package.json +1 -1
  96. package/packages/vibe64-runtime/src/server/codexAppServerProvider.js +34 -0
  97. package/packages/vibe64-sessions/package.json +1 -1
  98. package/packages/vibe64-sessions/src/server/sessionRenewal.js +4 -1
  99. package/packages/vibe64-source-editor/package.json +5 -5
  100. package/packages/vibe64-system-graph/package.json +3 -3
  101. package/packages/vibe64-terminals/package.json +2 -2
  102. package/packages/vibe64-terminals/src/server/agent/providers/codexSessionAgentProvider.js +6 -0
  103. package/packages/vibe64-terminals/src/server/agent/sessionAgentManager.js +16 -0
  104. package/packages/vibe64-terminals/src/server/codexTerminal.js +180 -64
  105. package/packages/vibe64-terminals/src/server/registerRoutes.js +12 -0
  106. package/packages/vibe64-terminals/src/server/service.js +57 -16
  107. package/packages/vibe64-terminals/src/server/sessionPromptHints.js +9 -1
  108. package/packages/vibe64-terminals/src/server/sessionSaveCommitMessage.js +1 -1
  109. package/dist/assets/Vibe64SessionFiles-BS5MW7Qz.js +0 -1
  110. package/dist/assets/_slug_-BzRzebKA.js +0 -12
  111. package/dist/assets/_slug_-Q7ibe3SE.css +0 -1
@@ -6900,19 +6900,21 @@ function createCodexTerminalController({
6900
6900
  const normalizedThreadId = normalizeText(threadId);
6901
6901
  const notificationTurnId = codexAppServerNotificationTurnId(notification);
6902
6902
  const goalStatus = normalizeText(notification.params?.goal?.status);
6903
+ const goalCleared = notification.method === "thread/goal/cleared";
6903
6904
  const runtime = await createRuntimeForSession();
6904
6905
  let run = await readCodexAppServerAgentRunForSession(
6905
6906
  runtime.store,
6906
6907
  normalizedSessionId
6907
6908
  );
6908
6909
  const currentThreadId = normalizeText(run?.providerThreadId);
6910
+ if (currentThreadId && currentThreadId !== normalizedThreadId) {
6911
+ return { ok: true, processed: false, reason: "goal_thread_changed" };
6912
+ }
6909
6913
  if (
6910
- run &&
6911
- CODEX_APP_SERVER_GOAL_STATUSES.has(goalStatus) &&
6912
- (!currentThreadId || currentThreadId === normalizedThreadId) &&
6914
+ (goalCleared || CODEX_APP_SERVER_GOAL_STATUSES.has(goalStatus)) &&
6913
6915
  (
6914
- normalizeText(run.providerGoalStatus) !== goalStatus ||
6915
- normalizeText(run.providerGoalThreadId) !== normalizedThreadId
6916
+ normalizeText(run?.providerGoalStatus) !== goalStatus ||
6917
+ normalizeText(run?.providerGoalThreadId) !== normalizedThreadId
6916
6918
  )
6917
6919
  ) {
6918
6920
  const updatedAt = new Date().toISOString();
@@ -6924,6 +6926,12 @@ function createCodexTerminalController({
6924
6926
  providerThreadId: normalizedThreadId
6925
6927
  },
6926
6928
  patch: {
6929
+ ...(!run ? {
6930
+ provider: CODEX_AGENT_PROVIDER,
6931
+ providerInterface: "codex_app_server",
6932
+ providerThreadId: normalizedThreadId,
6933
+ state: VIBE64_AGENT_RUN_STATE.COMPLETED
6934
+ } : {}),
6927
6935
  providerGoalStatus: goalStatus,
6928
6936
  providerGoalThreadId: normalizedThreadId,
6929
6937
  providerGoalUpdatedAt: updatedAt
@@ -6950,7 +6958,7 @@ function createCodexTerminalController({
6950
6958
  };
6951
6959
  }
6952
6960
  if (
6953
- CODEX_APP_SERVER_GOAL_STATUSES.has(goalStatus) &&
6961
+ (goalCleared || CODEX_APP_SERVER_GOAL_STATUSES.has(goalStatus)) &&
6954
6962
  goalStatus !== "active" &&
6955
6963
  turn.state === "finalizing" &&
6956
6964
  turn.threadId === normalizedThreadId &&
@@ -7044,8 +7052,16 @@ function createCodexTerminalController({
7044
7052
  );
7045
7053
  });
7046
7054
  }
7055
+ if (method === "thread/goal/cleared") {
7056
+ runCodexAppServerNotificationTask(notificationContext, async () => {
7057
+ await reconcileCodexAppServerGoalUpdated(normalizedSessionId, provider, normalizedThreadId, notification);
7058
+ await publishSessionChanged(normalizedSessionId, { reason: "codex-goal" });
7059
+ });
7060
+ return;
7061
+ }
7047
7062
  if (method === "thread/goal/updated") {
7048
- runCodexAppServerNotificationTask(notificationContext, () => {
7063
+ runCodexAppServerNotificationTask(notificationContext, async () => {
7064
+ await publishSessionChanged(normalizedSessionId, { reason: "codex-goal" });
7049
7065
  return reconcileCodexAppServerGoalUpdated(
7050
7066
  normalizedSessionId,
7051
7067
  provider,
@@ -8990,10 +9006,10 @@ function createCodexTerminalController({
8990
9006
  );
8991
9007
  error.code = "vibe64_codex_economy_ownership_blocked";
8992
9008
  error.statusCode = 409;
8993
- error.retryable = true;
9009
+ error.retryable = details.retryable !== false;
8994
9010
  error.details = {
8995
9011
  ...details,
8996
- retryable: true
9012
+ retryable: error.retryable
8997
9013
  };
8998
9014
  return error;
8999
9015
  }
@@ -9066,33 +9082,31 @@ function createCodexTerminalController({
9066
9082
  await pendingMutation.catch(() => null);
9067
9083
  }
9068
9084
  const existing = codexAppServerEconomyThreads.get(key);
9069
- if (existing) {
9070
- if (
9071
- existing.ownershipId !== record.ownershipId ||
9072
- existing.revision < record.revision
9073
- ) {
9074
- throw codexAppServerEconomyOwnershipError(
9075
- "Persisted Codex economy ownership changed while the controller was running.",
9076
- {
9077
- sessionId: record.sessionId,
9078
- threadId: record.threadId
9079
- }
9080
- );
9081
- }
9082
- if (
9083
- session.status !== VIBE64_SESSION_STATUS.ARCHIVED &&
9084
- !sessionIsClosing(session)
9085
- ) {
9085
+ if (existing && (
9086
+ existing.ownershipId !== record.ownershipId ||
9087
+ existing.revision < record.revision
9088
+ )) {
9089
+ throw codexAppServerEconomyOwnershipError(
9090
+ "Persisted Codex economy ownership changed while the controller was running.",
9091
+ { sessionId: record.sessionId, threadId: record.threadId }
9092
+ );
9093
+ }
9094
+ const cleanupRequired = record.lifecycle === CODEX_ECONOMY_THREAD_LIFECYCLES.CLEANUP_REQUIRED;
9095
+ if (existing && !cleanupRequired) {
9096
+ if (session.status !== VIBE64_SESSION_STATUS.ARCHIVED && !sessionIsClosing(session)) {
9086
9097
  return { record: existing, retiredThreadId: "" };
9087
9098
  }
9088
9099
  await retireCodexAppServerEconomyThread(existing);
9089
9100
  return { record: null, retiredThreadId: existing.threadId };
9090
9101
  }
9091
9102
  if (!await directoryExists(record.identity.runtime.runtimeDir)) {
9092
- await ledger.remove(record);
9103
+ if (existing) {
9104
+ await retireCodexAppServerEconomyThread(existing);
9105
+ } else {
9106
+ await ledger.remove(record);
9107
+ }
9093
9108
  return { record: null, retiredThreadId: record.threadId };
9094
9109
  }
9095
- const executionRoot = terminalSessionSourceRoot(session);
9096
9110
  const toolHome = await codexToolHomeResult();
9097
9111
  if (toolHome.ok === false) {
9098
9112
  throw codexAppServerEconomyOwnershipError(toolHome.error, {
@@ -9102,57 +9116,55 @@ function createCodexTerminalController({
9102
9116
  }
9103
9117
  const providerOptions = await codexAppServerEconomyRuntimeOptionsForSession(session, {
9104
9118
  runtime,
9105
- executionRoot,
9119
+ executionRoot: terminalSessionSourceRoot(session),
9106
9120
  toolHomeSource: toolHome.toolHomeSource,
9107
9121
  workdir: record.workdir
9108
9122
  });
9109
9123
  const providerKey = codexAppServerProviderKey(record.sessionId, providerOptions);
9110
- if (codexAppServerProviderKeyFingerprint(providerKey) !== record.identity.providerKeyFingerprint) {
9124
+ const contextChanged = !existing &&
9125
+ codexAppServerProviderKeyFingerprint(providerKey) !== record.identity.providerKeyFingerprint;
9126
+ if (cleanupRequired || contextChanged) {
9111
9127
  const staleProvider = codexAppServerProviderFactory({
9112
9128
  ...providerOptions,
9113
9129
  runtimeDir: record.identity.runtime.runtimeDir
9114
9130
  });
9115
9131
  try {
9116
- if (typeof staleProvider.currentRuntimeInfo !== "function") {
9117
- throw codexAppServerEconomyOwnershipError(
9118
- "The Codex provider cannot verify the account for stale economy ownership.",
9119
- {
9120
- sessionId: record.sessionId,
9121
- threadId: record.threadId
9122
- }
9123
- );
9124
- }
9125
9132
  const currentRuntime = await staleProvider.currentRuntimeInfo();
9126
- if (
9127
- record.identity.runtime.accountIdentitySignature !==
9128
- normalizeText(currentRuntime.accountIdentitySignature)
9129
- ) {
9133
+ const accountChanged = record.identity.runtime.accountIdentitySignature !==
9134
+ normalizeText(currentRuntime.accountIdentitySignature);
9135
+ if (accountChanged && !cleanupRequired) {
9130
9136
  throw codexAppServerEconomyOwnershipError(
9131
- "The current Codex account does not match persisted economy ownership.",
9132
- {
9133
- sessionId: record.sessionId,
9134
- threadId: record.threadId
9135
- }
9137
+ "Reconnect the original Codex account to resume or delete this helper thread.",
9138
+ { retryable: false, sessionId: record.sessionId, threadId: record.threadId }
9136
9139
  );
9137
9140
  }
9138
- const stopped = typeof staleProvider.stopRuntime === "function"
9139
- ? await staleProvider.stopRuntime()
9140
- : null;
9141
- if (!codexAppServerRuntimeStopWasVerified(stopped)) {
9142
- throw codexAppServerEconomyOwnershipError(
9143
- "The earlier Codex economy runtime could not be retired after its provider context changed.",
9144
- {
9145
- sessionId: record.sessionId,
9146
- threadId: record.threadId
9147
- }
9148
- );
9141
+ if (accountChanged || contextChanged) {
9142
+ // Only the local runtime owner can retire another account's ephemeral
9143
+ // work. Never issue a thread deletion authenticated as the new account.
9144
+ const stopped = await staleProvider.stopRuntime(accountChanged ? {
9145
+ expectedAccountIdentitySignature: record.identity.runtime.accountIdentitySignature
9146
+ } : {});
9147
+ const retired = accountChanged
9148
+ ? stopped?.ownershipSuperseded === true || stopped?.processExitVerified === true
9149
+ : codexAppServerRuntimeStopWasVerified(stopped);
9150
+ if (!retired) {
9151
+ throw codexAppServerEconomyOwnershipError(
9152
+ "The earlier Codex runtime could not be verified as retired; its cleanup record has been preserved.",
9153
+ { sessionId: record.sessionId, threadId: record.threadId }
9154
+ );
9155
+ }
9156
+ await ledger.remove(record);
9157
+ codexAppServerEconomyThreads.delete(key);
9158
+ return { record: null, retiredThreadId: record.threadId };
9149
9159
  }
9150
- await ledger.remove(record);
9151
- return { record: null, retiredThreadId: record.threadId };
9152
9160
  } finally {
9153
9161
  staleProvider.close?.();
9154
9162
  }
9155
9163
  }
9164
+ if (existing) {
9165
+ await retireCodexAppServerEconomyThread(existing);
9166
+ return { record: null, retiredThreadId: existing.threadId };
9167
+ }
9156
9168
  let provider = codexAppServerProviders.get(providerKey);
9157
9169
  if (!provider) {
9158
9170
  assertCodexAppServerControllerOpen();
@@ -9182,6 +9194,7 @@ function createCodexTerminalController({
9182
9194
  throw codexAppServerEconomyOwnershipError(
9183
9195
  "The current Codex runtime/auth identity does not match persisted economy ownership.",
9184
9196
  {
9197
+ retryable: record.identity.runtime.accountIdentitySignature === normalizeText(expectedRuntime.accountIdentitySignature),
9185
9198
  sessionId: record.sessionId,
9186
9199
  threadId: record.threadId
9187
9200
  }
@@ -9281,6 +9294,7 @@ function createCodexTerminalController({
9281
9294
  return !normalizedSessionId || record.sessionId === normalizedSessionId;
9282
9295
  });
9283
9296
  const retiredThreadIds = [];
9297
+ const retiredBySession = new Map();
9284
9298
  for (const record of records) {
9285
9299
  try {
9286
9300
  const currentSession = normalizeText(session?.sessionId || session?.id) === record.sessionId
@@ -9293,11 +9307,47 @@ function createCodexTerminalController({
9293
9307
  });
9294
9308
  if (normalizeText(restored?.retiredThreadId)) {
9295
9309
  retiredThreadIds.push(normalizeText(restored.retiredThreadId));
9310
+ const retired = retiredBySession.get(record.sessionId) || [];
9311
+ retired.push(normalizeText(restored.retiredThreadId));
9312
+ retiredBySession.set(record.sessionId, retired);
9296
9313
  }
9297
9314
  } catch (error) {
9298
9315
  failed.push(codexAppServerEconomyFailure(record, error));
9299
9316
  }
9300
9317
  }
9318
+ const sessionIds = new Set(records.map((record) => record.sessionId));
9319
+ if (normalizedSessionId) {
9320
+ sessionIds.add(normalizedSessionId);
9321
+ }
9322
+ for (const id of sessionIds) {
9323
+ const failures = failed.filter((failure) => !failure.sessionId || failure.sessionId === id);
9324
+ const retired = retiredBySession.get(id) || [];
9325
+ if (!failures.length && !retired.length) {
9326
+ continue;
9327
+ }
9328
+ const status = failures.length ? "failed" : "ready";
9329
+ const message = failures.length
9330
+ ? `${failures.length} helper cleanup record(s) still need attention: ${failures[0].error}`
9331
+ : `Cleaned up ${retired.length} stale helper ownership record(s).`;
9332
+ await effectiveRuntime.store.writeBackgroundTaskEvent(id, "codex-economy-cleanup", {
9333
+ event: {
9334
+ kind: "cleanup-reconciled",
9335
+ message,
9336
+ status,
9337
+ failed: failures,
9338
+ retiredThreadIds: retired
9339
+ },
9340
+ patch: {
9341
+ label: "Low-cost assistant cleanup",
9342
+ message,
9343
+ error: failures[0]?.error || "",
9344
+ code: failures[0]?.code || "",
9345
+ details: { failed: failures },
9346
+ status,
9347
+ retryable: failures.some((failure) => failure.retryable !== false)
9348
+ }
9349
+ });
9350
+ }
9301
9351
  return {
9302
9352
  failed,
9303
9353
  ok: failed.length === 0,
@@ -9324,6 +9374,7 @@ function createCodexTerminalController({
9324
9374
  "Vibe64 could not reconcile persisted low-cost assistant thread ownership.",
9325
9375
  {
9326
9376
  failed: result.failed,
9377
+ retryable: result.failed?.some((failure) => failure.retryable !== false) !== false,
9327
9378
  projectRuntimeRoot: normalizeText(result.projectRuntimeRoot)
9328
9379
  }
9329
9380
  );
@@ -9750,7 +9801,8 @@ function createCodexTerminalController({
9750
9801
  ? {}
9751
9802
  : await currentConversationActorMetadata(input.vibe64User || null),
9752
9803
  economyRestore,
9753
- provider
9804
+ provider,
9805
+ providerOptions: activeProvider?.providerOptions || providerOptions
9754
9806
  };
9755
9807
  }
9756
9808
 
@@ -12590,7 +12642,8 @@ function createCodexTerminalController({
12590
12642
  currentSession = await runtime.getSession(sessionId);
12591
12643
  turn = codexAppServerTurnState(currentSession);
12592
12644
  const turnId = normalizeText(turn.turnId);
12593
- if (!turn.active) {
12645
+ const goalAwaitingTurn = !turn.active && turn.goalStatus === "active" && turn.goalThreadId === threadId;
12646
+ if (!turn.active && !goalAwaitingTurn) {
12594
12647
  vibe64SessionDebugLog("server.codexTerminal.appServerMessage.newTurn", {
12595
12648
  messageId,
12596
12649
  reason: "provider_idle",
@@ -12604,7 +12657,7 @@ function createCodexTerminalController({
12604
12657
  turnId
12605
12658
  });
12606
12659
  }
12607
- if (!turnId || turn.state === "finalizing") {
12660
+ if (goalAwaitingTurn || !turnId || turn.state === "finalizing") {
12608
12661
  return withCodexState({
12609
12662
  code: CODEX_AGENT_TURN_STEER_FAILED_CODE,
12610
12663
  delivered: false,
@@ -13099,6 +13152,69 @@ function createCodexTerminalController({
13099
13152
  });
13100
13153
  },
13101
13154
 
13155
+ async readGoal(sessionId, options = {}) {
13156
+ const context = await codexAppServerSessionContext(sessionId, options);
13157
+ if (context.ok === false) {
13158
+ return { status: "unavailable", goal: null };
13159
+ }
13160
+ const threadId = codexAppServerTurnState(context.session).threadId ||
13161
+ codexThreadIdForWorkdir(context.session, context.workdir);
13162
+ if (!threadId) {
13163
+ return { status: "available", goal: null };
13164
+ }
13165
+ const sessionKey = codexTerminalNamespace(sessionId);
13166
+ for (const [key, provider] of codexAppServerProviders) {
13167
+ if (codexAppServerProviderSessionKeys.get(key) === sessionKey &&
13168
+ !provider.isEconomyProvider() && provider.isAvailable()) {
13169
+ const result = await provider.readGoal(threadId);
13170
+ return { status: "available", goal: result.goal || null };
13171
+ }
13172
+ }
13173
+ return { status: "unavailable", goal: null };
13174
+ },
13175
+
13176
+ async updateGoal(sessionId, input = {}, options = {}) {
13177
+ if (!["pause", "resume"].includes(input.action)) {
13178
+ return { ok: false, error: "Choose pause or resume." };
13179
+ }
13180
+ const admission = beginTerminalNamespaceOperation(codexTerminalNamespace(sessionId));
13181
+ if (admission.ok === false) return admission;
13182
+ try {
13183
+ const context = await codexAppServerConversationContext(sessionId, {}, options);
13184
+ if (context.ok === false) return context;
13185
+ const threadId = codexAppServerTurnState(context.session).threadId ||
13186
+ codexThreadIdForWorkdir(context.session, context.workdir);
13187
+ if (!threadId || threadId !== input.threadId) {
13188
+ return { ok: false, error: "The Codex conversation changed. Refresh the goal before trying again." };
13189
+ }
13190
+ const { goal } = await context.provider.readGoal(threadId);
13191
+ if (!goal || goal.status === "complete" || goal.createdAt !== input.createdAt || goal.objective !== input.objective) {
13192
+ return { ok: false, error: "The Codex goal changed. Refresh it before trying again." };
13193
+ }
13194
+ if (input.action === "pause" && !["active", "paused"].includes(goal.status)) {
13195
+ return { ok: false, error: "This goal is already stopped. Refresh its details." };
13196
+ }
13197
+ if (input.action === "resume" && !["paused", "blocked", "usageLimited"].includes(goal.status)) {
13198
+ return { ok: false, error: "This goal cannot be resumed from its current status. Refresh its details." };
13199
+ }
13200
+ subscribeCodexAppServerEvents(sessionId, context.provider, threadId, context.providerOptions);
13201
+ const result = await context.provider.setGoalStatus(threadId, input.action === "pause" ? "paused" : "active");
13202
+ await reconcileCodexAppServerGoalUpdated(sessionId, context.provider, threadId, {
13203
+ params: { goal: result.goal }
13204
+ });
13205
+ if (input.action === "pause") {
13206
+ const stopped = await interruptCodexAppServerTurnWithinAdmission(sessionId, { threadId });
13207
+ if (stopped.ok === false) {
13208
+ return { ...stopped, goal: result.goal };
13209
+ }
13210
+ }
13211
+ await publishSessionChanged(sessionId, { reason: "codex-goal" });
13212
+ return { ok: true, status: "available", goal: result.goal || null };
13213
+ } finally {
13214
+ admission.release();
13215
+ }
13216
+ },
13217
+
13102
13218
  async readPlanUsage(sessionId) {
13103
13219
  const sessionKey = codexTerminalNamespace(sessionId);
13104
13220
  for (const [key, provider] of codexAppServerProviders) {
@@ -313,6 +313,18 @@ function registerRoutes(
313
313
  );
314
314
  });
315
315
 
316
+ routes.serviceRoute("GET", "/sessions/:sessionId/agent-goal", {
317
+ summary: "Read the selected Codex conversation goal."
318
+ }, (request) => terminalService().readAgentGoal(
319
+ request.params.sessionId, withVibe64User(request, {})
320
+ ));
321
+
322
+ routes.serviceRoute("POST", "/sessions/:sessionId/agent-goal", {
323
+ summary: "Pause or resume the selected Codex conversation goal."
324
+ }, (request) => terminalService().updateAgentGoal(
325
+ request.params.sessionId, withVibe64User(request, routes.requestBody(request))
326
+ ));
327
+
316
328
  routes.serviceRoute("GET", "/sessions/:sessionId/agent-plan-usage", {
317
329
  summary: "Read the selected assistant account's remaining plan allowance."
318
330
  }, (request) => terminalService().readAgentPlanUsage(
@@ -241,6 +241,11 @@ function timestampIso(ms = 0) {
241
241
  }
242
242
 
243
243
  function agentRunIsActive(run = {}) {
244
+ // Codex acknowledges Resume before its scheduler starts the next turn.
245
+ if (run?.providerGoalStatus === "active" && run.providerGoalThreadId &&
246
+ run.providerGoalThreadId === run.providerThreadId) {
247
+ return true;
248
+ }
244
249
  if (run?.active === true) {
245
250
  return true;
246
251
  }
@@ -1711,22 +1716,45 @@ function createService({
1711
1716
  session,
1712
1717
  vibe64User: input.vibe64User || null
1713
1718
  };
1714
- const providerDescription = await sessionAgent.describeProvider(agentContext);
1715
- const commitTitle = await generateSessionSaveCommitMessage({
1716
- agentContext,
1717
- changes: saveMessageInput.changes,
1718
- deleteThread: (threadInput, options) => sessionAgent.deleteDetachedChatThread(
1719
- normalizedSessionId,
1720
- threadInput,
1721
- options
1722
- ),
1723
- runAgentTurn: (turnInput, options) => sessionAgent.streamDetachedChatTurn(
1724
- normalizedSessionId,
1725
- turnInput,
1726
- options
1727
- ),
1728
- expectedAccountIdentitySignature: providerDescription.accountIdentitySignature
1729
- });
1719
+ let commitTitle;
1720
+ try {
1721
+ const providerDescription = await sessionAgent.describeProvider(agentContext);
1722
+ commitTitle = await generateSessionSaveCommitMessage({
1723
+ agentContext,
1724
+ changes: saveMessageInput.changes,
1725
+ deleteThread: (threadInput, options) => sessionAgent.deleteDetachedChatThread(
1726
+ normalizedSessionId,
1727
+ threadInput,
1728
+ options
1729
+ ),
1730
+ runAgentTurn: (turnInput, options) => sessionAgent.streamDetachedChatTurn(
1731
+ normalizedSessionId,
1732
+ turnInput,
1733
+ options
1734
+ ),
1735
+ expectedAccountIdentitySignature: providerDescription.accountIdentitySignature
1736
+ });
1737
+ } catch (error) {
1738
+ // Naming is optional. Leave failed thread ownership intact and let the
1739
+ // repository owner enforce the normal checkpoint and publish checks.
1740
+ commitTitle = {
1741
+ executionProfile: null,
1742
+ subject: `Save work ${saveMessageInput.checkpoint.checkpointTree.slice(0, 12)}`
1743
+ };
1744
+ logOperationalEvent(logger, "warn", {
1745
+ code: error.code || "vibe64_session_save_message_failed",
1746
+ component: "vibe64.session_save",
1747
+ event: "vibe64.session_save.message_fallback",
1748
+ operationId: input.operationId,
1749
+ sessionId: normalizedSessionId
1750
+ }, "Assistant naming was unavailable; Save is using a checkpoint-based version name.");
1751
+ await input.onProgress?.({
1752
+ code: error.code || "vibe64_session_save_message_failed",
1753
+ kind: "message",
1754
+ message: "Assistant naming is unavailable. Saving with a checkpoint-based version name.",
1755
+ stage: "message-fallback"
1756
+ });
1757
+ }
1730
1758
  await input.onProgress?.({
1731
1759
  executionProfile: commitTitle.executionProfile,
1732
1760
  kind: "message",
@@ -2381,6 +2409,19 @@ function createService({
2381
2409
  }
2382
2410
  },
2383
2411
 
2412
+ async readAgentGoal(sessionId, options = {}) {
2413
+ return sessionAgent.readGoal(sessionId, await assistantSessionOptions(sessionId, options));
2414
+ },
2415
+
2416
+ async updateAgentGoal(sessionId, input = {}) {
2417
+ if (input.action === "resume") {
2418
+ return runMainAgentWrite(sessionId, input, (context) => (
2419
+ sessionAgent.updateGoal(sessionId, input, context)
2420
+ ), { operation: "resume-agent-goal" });
2421
+ }
2422
+ return sessionAgent.updateGoal(sessionId, input, await assistantSessionOptions(sessionId, input));
2423
+ },
2424
+
2384
2425
  async readAgentPlanUsage(sessionId, options = {}) {
2385
2426
  return sessionAgent.readPlanUsage(sessionId, await assistantSessionOptions(sessionId, options));
2386
2427
  },
@@ -636,7 +636,14 @@ function createSessionPromptHintsService({
636
636
  executionProfile: PROMPT_HINT_EXECUTION_PROFILE_REQUEST,
637
637
  threadId: job.threadId,
638
638
  turnId: job.turnId
639
- }, agentOptions(job.context, job.vibe64User))).catch((error) => {
639
+ }, agentOptions(job.context, job.vibe64User))).then((result) => {
640
+ if (result?.ok !== true) {
641
+ const error = new Error(result?.error || "Prompt hint interruption was not confirmed.");
642
+ error.code = result?.code || "vibe64_prompt_hints_interrupt_unconfirmed";
643
+ throw error;
644
+ }
645
+ return result;
646
+ }).catch((error) => {
640
647
  reportDiagnostic("vibe64_prompt_hints_interrupt_failed", error, {
641
648
  sessionId: job.sessionId
642
649
  });
@@ -691,6 +698,7 @@ function createSessionPromptHintsService({
691
698
  } finally {
692
699
  if (job.threadId) {
693
700
  try {
701
+ await job.interruptPromise;
694
702
  const cleanup = await deleteAgentThread(job.sessionId, {
695
703
  executionProfile: PROMPT_HINT_EXECUTION_PROFILE_REQUEST,
696
704
  threadId: job.threadId
@@ -155,7 +155,7 @@ async function cleanupSessionSaveCommitMessageThread({
155
155
  [
156
156
  "The temporary naming conversation could not be removed.",
157
157
  detail,
158
- "Retry Save to finish cleanup before publishing work."
158
+ "Cleanup will be retried on the next helper operation; Save can use a checkpoint-based name."
159
159
  ].filter(Boolean).join(" "),
160
160
  text(cleanup?.code) || "vibe64_session_save_message_cleanup_failed"
161
161
  );