@bohuyeshan/openagent-labforge-core 3.13.1 → 3.13.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/dist/index.js CHANGED
@@ -61837,6 +61837,9 @@ function isAbortError(error48) {
61837
61837
  return false;
61838
61838
  }
61839
61839
 
61840
+ // src/hooks/atlas/idle-event.ts
61841
+ init_logger();
61842
+
61840
61843
  // src/hooks/atlas/boulder-continuation-injector.ts
61841
61844
  init_logger();
61842
61845
 
@@ -62143,10 +62146,196 @@ async function getLastAgentFromSession(sessionID, client) {
62143
62146
  return getLastAgentFromMessageDir(messageDir);
62144
62147
  }
62145
62148
 
62146
- // src/hooks/atlas/event-handler.ts
62149
+ // src/hooks/atlas/resolve-active-boulder-session.ts
62150
+ async function resolveActiveBoulderSession(input) {
62151
+ const boulderState = readBoulderState(input.directory);
62152
+ if (!boulderState) {
62153
+ return null;
62154
+ }
62155
+ const progress = getPlanProgress(boulderState.active_plan);
62156
+ if (progress.isComplete) {
62157
+ return { boulderState, progress, appendedSession: false };
62158
+ }
62159
+ if (boulderState.session_ids.includes(input.sessionID)) {
62160
+ return { boulderState, progress, appendedSession: false };
62161
+ }
62162
+ if (!subagentSessions.has(input.sessionID)) {
62163
+ return null;
62164
+ }
62165
+ const updatedBoulderState = appendSessionId(input.directory, input.sessionID);
62166
+ if (!updatedBoulderState?.session_ids.includes(input.sessionID)) {
62167
+ return null;
62168
+ }
62169
+ return {
62170
+ boulderState: updatedBoulderState,
62171
+ progress,
62172
+ appendedSession: true
62173
+ };
62174
+ }
62175
+
62176
+ // src/hooks/atlas/idle-event.ts
62147
62177
  var CONTINUATION_COOLDOWN_MS2 = 5000;
62148
62178
  var FAILURE_BACKOFF_MS = 5 * 60 * 1000;
62149
62179
  var RETRY_DELAY_MS = CONTINUATION_COOLDOWN_MS2 + 1000;
62180
+ function hasRunningBackgroundTasks(sessionID, options) {
62181
+ const backgroundManager = options?.backgroundManager;
62182
+ return backgroundManager ? backgroundManager.getTasksByParentSession(sessionID).some((task) => task.status === "running") : false;
62183
+ }
62184
+ async function injectContinuation2(input) {
62185
+ const remaining = input.progress.total - input.progress.completed;
62186
+ input.sessionState.lastContinuationInjectedAt = Date.now();
62187
+ try {
62188
+ await injectBoulderContinuation({
62189
+ ctx: input.ctx,
62190
+ sessionID: input.sessionID,
62191
+ planName: input.planName,
62192
+ remaining,
62193
+ total: input.progress.total,
62194
+ agent: input.agent,
62195
+ worktreePath: input.worktreePath,
62196
+ backgroundManager: input.options?.backgroundManager,
62197
+ sessionState: input.sessionState
62198
+ });
62199
+ } catch (error48) {
62200
+ log(`[${HOOK_NAME7}] Failed to inject boulder continuation`, { sessionID: input.sessionID, error: error48 });
62201
+ input.sessionState.promptFailureCount += 1;
62202
+ }
62203
+ }
62204
+ function scheduleRetry(input) {
62205
+ const { ctx, sessionID, sessionState, options } = input;
62206
+ if (sessionState.pendingRetryTimer) {
62207
+ return;
62208
+ }
62209
+ sessionState.pendingRetryTimer = setTimeout(async () => {
62210
+ sessionState.pendingRetryTimer = undefined;
62211
+ if (sessionState.promptFailureCount >= 2)
62212
+ return;
62213
+ const currentBoulder = await resolveActiveBoulderSession({
62214
+ client: ctx.client,
62215
+ directory: ctx.directory,
62216
+ sessionID
62217
+ });
62218
+ if (!currentBoulder)
62219
+ return;
62220
+ if (currentBoulder.progress.isComplete)
62221
+ return;
62222
+ if (options?.isContinuationStopped?.(sessionID))
62223
+ return;
62224
+ if (hasRunningBackgroundTasks(sessionID, options))
62225
+ return;
62226
+ await injectContinuation2({
62227
+ ctx,
62228
+ sessionID,
62229
+ sessionState,
62230
+ options,
62231
+ planName: currentBoulder.boulderState.plan_name,
62232
+ progress: currentBoulder.progress,
62233
+ agent: currentBoulder.boulderState.agent,
62234
+ worktreePath: currentBoulder.boulderState.worktree_path
62235
+ });
62236
+ }, RETRY_DELAY_MS);
62237
+ }
62238
+ async function handleAtlasSessionIdle(input) {
62239
+ const { ctx, options, getState, sessionID } = input;
62240
+ log(`[${HOOK_NAME7}] session.idle`, { sessionID });
62241
+ const activeBoulderSession = await resolveActiveBoulderSession({
62242
+ client: ctx.client,
62243
+ directory: ctx.directory,
62244
+ sessionID
62245
+ });
62246
+ if (!activeBoulderSession) {
62247
+ log(`[${HOOK_NAME7}] Skipped: not boulder or background task session`, { sessionID });
62248
+ return;
62249
+ }
62250
+ const { boulderState, progress } = activeBoulderSession;
62251
+ if (progress.isComplete) {
62252
+ log(`[${HOOK_NAME7}] Boulder complete`, { sessionID, plan: boulderState.plan_name });
62253
+ return;
62254
+ }
62255
+ const sessionState = getState(sessionID);
62256
+ const now = Date.now();
62257
+ if (sessionState.lastEventWasAbortError) {
62258
+ sessionState.lastEventWasAbortError = false;
62259
+ log(`[${HOOK_NAME7}] Skipped: abort error immediately before idle`, { sessionID });
62260
+ return;
62261
+ }
62262
+ if (sessionState.promptFailureCount >= 2) {
62263
+ const timeSinceLastFailure = sessionState.lastFailureAt !== undefined ? now - sessionState.lastFailureAt : Number.POSITIVE_INFINITY;
62264
+ if (timeSinceLastFailure < FAILURE_BACKOFF_MS) {
62265
+ log(`[${HOOK_NAME7}] Skipped: continuation in backoff after repeated failures`, {
62266
+ sessionID,
62267
+ promptFailureCount: sessionState.promptFailureCount,
62268
+ backoffRemaining: FAILURE_BACKOFF_MS - timeSinceLastFailure
62269
+ });
62270
+ return;
62271
+ }
62272
+ sessionState.promptFailureCount = 0;
62273
+ sessionState.lastFailureAt = undefined;
62274
+ }
62275
+ if (hasRunningBackgroundTasks(sessionID, options)) {
62276
+ log(`[${HOOK_NAME7}] Skipped: background tasks running`, { sessionID });
62277
+ return;
62278
+ }
62279
+ if (options?.isContinuationStopped?.(sessionID)) {
62280
+ log(`[${HOOK_NAME7}] Skipped: continuation stopped for session`, { sessionID });
62281
+ return;
62282
+ }
62283
+ const sessionAgent = getSessionAgent(sessionID);
62284
+ const lastAgent = await getLastAgentFromSession(sessionID, ctx.client);
62285
+ const effectiveAgent = sessionAgent ?? lastAgent;
62286
+ const lastAgentKey = getAgentConfigKey(effectiveAgent ?? "");
62287
+ const configuredBoulderAgent = boulderState.agent;
62288
+ const requiredAgentName = configuredBoulderAgent ?? (isAgentRegistered("atlas") ? "atlas" : undefined);
62289
+ if (!requiredAgentName) {
62290
+ log(`[${HOOK_NAME7}] Skipped: boulder agent is unavailable for continuation`, {
62291
+ sessionID,
62292
+ requiredAgent: requiredAgentName ?? "unknown"
62293
+ });
62294
+ return;
62295
+ }
62296
+ if (!configuredBoulderAgent && !isAgentRegistered(requiredAgentName)) {
62297
+ log(`[${HOOK_NAME7}] Skipped: implicit boulder agent is unavailable for continuation`, {
62298
+ sessionID,
62299
+ requiredAgent: requiredAgentName
62300
+ });
62301
+ return;
62302
+ }
62303
+ const requiredAgent = getAgentConfigKey(requiredAgentName);
62304
+ const lastAgentMatchesRequired = lastAgentKey === requiredAgent;
62305
+ const boulderAgentDefaultsToAtlas = requiredAgent === "atlas";
62306
+ const lastAgentIsSisyphus = lastAgentKey === "sisyphus";
62307
+ const allowSisyphusForAtlasBoulder = boulderAgentDefaultsToAtlas && lastAgentIsSisyphus;
62308
+ const agentMatches = lastAgentMatchesRequired || allowSisyphusForAtlasBoulder;
62309
+ if (!agentMatches) {
62310
+ log(`[${HOOK_NAME7}] Skipped: last agent does not match boulder agent`, {
62311
+ sessionID,
62312
+ lastAgent: effectiveAgent ?? "unknown",
62313
+ requiredAgent
62314
+ });
62315
+ return;
62316
+ }
62317
+ if (sessionState.lastContinuationInjectedAt && now - sessionState.lastContinuationInjectedAt < CONTINUATION_COOLDOWN_MS2) {
62318
+ scheduleRetry({ ctx, sessionID, sessionState, options });
62319
+ log(`[${HOOK_NAME7}] Skipped: continuation cooldown active`, {
62320
+ sessionID,
62321
+ cooldownRemaining: CONTINUATION_COOLDOWN_MS2 - (now - sessionState.lastContinuationInjectedAt),
62322
+ pendingRetry: !!sessionState.pendingRetryTimer
62323
+ });
62324
+ return;
62325
+ }
62326
+ await injectContinuation2({
62327
+ ctx,
62328
+ sessionID,
62329
+ sessionState,
62330
+ options,
62331
+ planName: boulderState.plan_name,
62332
+ progress,
62333
+ agent: boulderState.agent,
62334
+ worktreePath: boulderState.worktree_path
62335
+ });
62336
+ }
62337
+
62338
+ // src/hooks/atlas/event-handler.ts
62150
62339
  function createAtlasEventHandler(input) {
62151
62340
  const { ctx, options, sessions, getState } = input;
62152
62341
  return async ({ event }) => {
@@ -62165,143 +62354,7 @@ function createAtlasEventHandler(input) {
62165
62354
  const sessionID = props?.sessionID;
62166
62355
  if (!sessionID)
62167
62356
  return;
62168
- log(`[${HOOK_NAME7}] session.idle`, { sessionID });
62169
- const boulderState = readBoulderState(ctx.directory);
62170
- const isBoulderSession = boulderState?.session_ids?.includes(sessionID) ?? false;
62171
- const isBackgroundTaskSession = subagentSessions.has(sessionID);
62172
- if (!isBackgroundTaskSession && !isBoulderSession) {
62173
- log(`[${HOOK_NAME7}] Skipped: not boulder or background task session`, { sessionID });
62174
- return;
62175
- }
62176
- const state3 = getState(sessionID);
62177
- const now = Date.now();
62178
- if (state3.lastEventWasAbortError) {
62179
- state3.lastEventWasAbortError = false;
62180
- log(`[${HOOK_NAME7}] Skipped: abort error immediately before idle`, { sessionID });
62181
- return;
62182
- }
62183
- if (state3.promptFailureCount >= 2) {
62184
- const timeSinceLastFailure = state3.lastFailureAt !== undefined ? now - state3.lastFailureAt : Number.POSITIVE_INFINITY;
62185
- if (timeSinceLastFailure < FAILURE_BACKOFF_MS) {
62186
- log(`[${HOOK_NAME7}] Skipped: continuation in backoff after repeated failures`, {
62187
- sessionID,
62188
- promptFailureCount: state3.promptFailureCount,
62189
- backoffRemaining: FAILURE_BACKOFF_MS - timeSinceLastFailure
62190
- });
62191
- return;
62192
- }
62193
- state3.promptFailureCount = 0;
62194
- state3.lastFailureAt = undefined;
62195
- }
62196
- const backgroundManager = options?.backgroundManager;
62197
- const hasRunningBgTasks = backgroundManager ? backgroundManager.getTasksByParentSession(sessionID).some((t) => t.status === "running") : false;
62198
- if (hasRunningBgTasks) {
62199
- log(`[${HOOK_NAME7}] Skipped: background tasks running`, { sessionID });
62200
- return;
62201
- }
62202
- if (!boulderState) {
62203
- log(`[${HOOK_NAME7}] No active boulder`, { sessionID });
62204
- return;
62205
- }
62206
- if (options?.isContinuationStopped?.(sessionID)) {
62207
- log(`[${HOOK_NAME7}] Skipped: continuation stopped for session`, { sessionID });
62208
- return;
62209
- }
62210
- const sessionAgent = getSessionAgent(sessionID);
62211
- const lastAgent = await getLastAgentFromSession(sessionID, ctx.client);
62212
- const effectiveAgent = sessionAgent ?? lastAgent;
62213
- const lastAgentKey = getAgentConfigKey(effectiveAgent ?? "");
62214
- const requiredAgentName = boulderState.agent ?? (isAgentRegistered("atlas") ? "atlas" : undefined);
62215
- if (!requiredAgentName || !isAgentRegistered(requiredAgentName)) {
62216
- log(`[${HOOK_NAME7}] Skipped: boulder agent is unavailable for continuation`, {
62217
- sessionID,
62218
- requiredAgent: requiredAgentName ?? "unknown"
62219
- });
62220
- return;
62221
- }
62222
- const requiredAgent = getAgentConfigKey(requiredAgentName);
62223
- const lastAgentMatchesRequired = lastAgentKey === requiredAgent;
62224
- const boulderAgentDefaultsToAtlas = requiredAgent === "atlas";
62225
- const lastAgentIsSisyphus = lastAgentKey === "sisyphus";
62226
- const allowSisyphusForAtlasBoulder = boulderAgentDefaultsToAtlas && lastAgentIsSisyphus;
62227
- const agentMatches = lastAgentMatchesRequired || allowSisyphusForAtlasBoulder;
62228
- if (!agentMatches) {
62229
- log(`[${HOOK_NAME7}] Skipped: last agent does not match boulder agent`, {
62230
- sessionID,
62231
- lastAgent: effectiveAgent ?? "unknown",
62232
- requiredAgent
62233
- });
62234
- return;
62235
- }
62236
- const progress = getPlanProgress(boulderState.active_plan);
62237
- if (progress.isComplete) {
62238
- log(`[${HOOK_NAME7}] Boulder complete`, { sessionID, plan: boulderState.plan_name });
62239
- return;
62240
- }
62241
- if (state3.lastContinuationInjectedAt && now - state3.lastContinuationInjectedAt < CONTINUATION_COOLDOWN_MS2) {
62242
- if (!state3.pendingRetryTimer) {
62243
- state3.pendingRetryTimer = setTimeout(async () => {
62244
- state3.pendingRetryTimer = undefined;
62245
- if (state3.promptFailureCount >= 2)
62246
- return;
62247
- const currentBoulder = readBoulderState(ctx.directory);
62248
- if (!currentBoulder)
62249
- return;
62250
- if (!currentBoulder.session_ids?.includes(sessionID))
62251
- return;
62252
- const currentProgress = getPlanProgress(currentBoulder.active_plan);
62253
- if (currentProgress.isComplete)
62254
- return;
62255
- if (options?.isContinuationStopped?.(sessionID))
62256
- return;
62257
- const hasBgTasks = backgroundManager ? backgroundManager.getTasksByParentSession(sessionID).some((t) => t.status === "running") : false;
62258
- if (hasBgTasks)
62259
- return;
62260
- state3.lastContinuationInjectedAt = Date.now();
62261
- const currentRemaining = currentProgress.total - currentProgress.completed;
62262
- try {
62263
- await injectBoulderContinuation({
62264
- ctx,
62265
- sessionID,
62266
- planName: currentBoulder.plan_name,
62267
- remaining: currentRemaining,
62268
- total: currentProgress.total,
62269
- agent: currentBoulder.agent,
62270
- worktreePath: currentBoulder.worktree_path,
62271
- backgroundManager,
62272
- sessionState: state3
62273
- });
62274
- } catch (err) {
62275
- log(`[${HOOK_NAME7}] Delayed retry failed`, { sessionID, error: err });
62276
- state3.promptFailureCount++;
62277
- }
62278
- }, RETRY_DELAY_MS);
62279
- }
62280
- log(`[${HOOK_NAME7}] Skipped: continuation cooldown active`, {
62281
- sessionID,
62282
- cooldownRemaining: CONTINUATION_COOLDOWN_MS2 - (now - state3.lastContinuationInjectedAt),
62283
- pendingRetry: !!state3.pendingRetryTimer
62284
- });
62285
- return;
62286
- }
62287
- state3.lastContinuationInjectedAt = now;
62288
- const remaining = progress.total - progress.completed;
62289
- try {
62290
- await injectBoulderContinuation({
62291
- ctx,
62292
- sessionID,
62293
- planName: boulderState.plan_name,
62294
- remaining,
62295
- total: progress.total,
62296
- agent: boulderState.agent,
62297
- worktreePath: boulderState.worktree_path,
62298
- backgroundManager,
62299
- sessionState: state3
62300
- });
62301
- } catch (err) {
62302
- log(`[${HOOK_NAME7}] Failed to inject boulder continuation`, { sessionID, error: err });
62303
- state3.promptFailureCount++;
62304
- }
62357
+ await handleAtlasSessionIdle({ ctx, options, getState, sessionID });
62305
62358
  return;
62306
62359
  }
62307
62360
  if (event.type === "message.updated") {
@@ -96096,7 +96149,8 @@ async function applyAgentConfig(params) {
96096
96149
  const isSisyphusEnabled = params.pluginConfig.sisyphus_agent?.disabled !== true;
96097
96150
  const builderEnabled = params.pluginConfig.sisyphus_agent?.default_builder_enabled ?? false;
96098
96151
  const plannerEnabled = params.pluginConfig.sisyphus_agent?.planner_enabled ?? true;
96099
- const replacePlan = params.pluginConfig.sisyphus_agent?.replace_plan ?? true;
96152
+ const replacePlan = params.pluginConfig.sisyphus_agent?.replace_plan ?? false;
96153
+ const hijackBuild = params.pluginConfig.sisyphus_agent?.hijack_build ?? false;
96100
96154
  const shouldDemotePlan = plannerEnabled && replacePlan;
96101
96155
  const configuredDefaultAgent = getConfiguredDefaultAgent(params.config);
96102
96156
  if (isSisyphusEnabled && builtinAgents.sisyphus) {
@@ -96129,7 +96183,7 @@ async function applyAgentConfig(params) {
96129
96183
  });
96130
96184
  }
96131
96185
  const filteredConfigAgents = configAgent ? Object.fromEntries(Object.entries(configAgent).filter(([key]) => {
96132
- if (key === "build")
96186
+ if (key === "build" && hijackBuild)
96133
96187
  return false;
96134
96188
  if (key === "plan" && shouldDemotePlan)
96135
96189
  return false;
@@ -96156,7 +96210,7 @@ async function applyAgentConfig(params) {
96156
96210
  ...filterDisabledAgents(filteredProjectAgents),
96157
96211
  ...filterDisabledAgents(filteredPluginAgents),
96158
96212
  ...filteredConfigAgents,
96159
- build: { ...migratedBuild, mode: "subagent", hidden: true },
96213
+ ...hijackBuild ? { build: { ...migratedBuild, mode: "subagent", hidden: true } } : {},
96160
96214
  ...planDemoteConfig ? { plan: planDemoteConfig } : {}
96161
96215
  };
96162
96216
  } else {
package/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@bohuyeshan/openagent-labforge-core",
3
- "version": "3.13.1",
3
+ "version": "3.13.3",
4
4
  "description": "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
5
- "main": "./dist/index.js",
6
- "types": "./dist/index.d.ts",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
7
  "type": "module",
8
- "bin": {
9
- "openagent-labforge": "bin/openagent-labforge.js"
10
- },
11
- "oc-plugin": [
12
- "server",
13
- "tui"
14
- ],
8
+ "bin": {
9
+ "openagent-labforge": "bin/openagent-labforge.js"
10
+ },
11
+ "oc-plugin": [
12
+ "server",
13
+ "tui"
14
+ ],
15
15
  "files": [
16
16
  "dist",
17
17
  "bin",
@@ -50,14 +50,14 @@
50
50
  ],
51
51
  "author": "YeonGyu-Kim",
52
52
  "license": "SUL-1.0",
53
- "repository": {
54
- "type": "git",
55
- "url": "git+https://github.com/BOHUYESHAN-APB/openagent-labforge.git"
56
- },
57
- "bugs": {
58
- "url": "https://github.com/BOHUYESHAN-APB/openagent-labforge/issues"
59
- },
60
- "homepage": "https://github.com/BOHUYESHAN-APB/openagent-labforge#readme",
53
+ "repository": {
54
+ "type": "git",
55
+ "url": "git+https://github.com/BOHUYESHAN-APB/openagent-labforge.git"
56
+ },
57
+ "bugs": {
58
+ "url": "https://github.com/BOHUYESHAN-APB/openagent-labforge/issues"
59
+ },
60
+ "homepage": "https://github.com/BOHUYESHAN-APB/openagent-labforge#readme",
61
61
  "dependencies": {
62
62
  "@ast-grep/cli": "^0.40.0",
63
63
  "@ast-grep/napi": "^0.40.0",
@@ -83,18 +83,18 @@
83
83
  "typescript": "^5.7.3"
84
84
  },
85
85
  "optionalDependencies": {
86
- "openagent-labforge-darwin-arm64": "3.11.2",
87
- "openagent-labforge-darwin-x64": "3.11.2",
88
- "openagent-labforge-darwin-x64-baseline": "3.11.2",
89
- "openagent-labforge-linux-arm64": "3.11.2",
90
- "openagent-labforge-linux-arm64-musl": "3.11.2",
91
- "openagent-labforge-linux-x64": "3.11.2",
92
- "openagent-labforge-linux-x64-baseline": "3.11.2",
93
- "openagent-labforge-linux-x64-musl": "3.11.2",
94
- "openagent-labforge-linux-x64-musl-baseline": "3.11.2",
95
- "openagent-labforge-windows-x64": "3.13.1",
96
- "openagent-labforge-windows-x64-baseline": "3.11.2"
97
- },
86
+ "openagent-labforge-darwin-arm64": "3.11.2",
87
+ "openagent-labforge-darwin-x64": "3.11.2",
88
+ "openagent-labforge-darwin-x64-baseline": "3.11.2",
89
+ "openagent-labforge-linux-arm64": "3.11.2",
90
+ "openagent-labforge-linux-arm64-musl": "3.11.2",
91
+ "openagent-labforge-linux-x64": "3.11.2",
92
+ "openagent-labforge-linux-x64-baseline": "3.11.2",
93
+ "openagent-labforge-linux-x64-musl": "3.11.2",
94
+ "openagent-labforge-linux-x64-musl-baseline": "3.11.2",
95
+ "openagent-labforge-windows-x64": "3.13.3",
96
+ "openagent-labforge-windows-x64-baseline": "3.11.2"
97
+ },
98
98
  "overrides": {
99
99
  "@opencode-ai/sdk": "^1.2.17"
100
100
  },