@opengoat/core 2026.2.18-2 → 2026.2.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.
Files changed (45) hide show
  1. package/dist/core/agents/application/agent.service.d.ts +11 -0
  2. package/dist/core/agents/application/agent.service.js +129 -15
  3. package/dist/core/agents/application/agent.service.js.map +1 -1
  4. package/dist/core/boards/application/board.service.d.ts +8 -1
  5. package/dist/core/boards/application/board.service.js +262 -38
  6. package/dist/core/boards/application/board.service.js.map +1 -1
  7. package/dist/core/boards/domain/board.d.ts +1 -2
  8. package/dist/core/bootstrap/application/bootstrap.service.js +4 -0
  9. package/dist/core/bootstrap/application/bootstrap.service.js.map +1 -1
  10. package/dist/core/opengoat/application/opengoat.service.d.ts +6 -1
  11. package/dist/core/opengoat/application/opengoat.service.helpers.d.ts +52 -0
  12. package/dist/core/opengoat/application/opengoat.service.helpers.js +129 -3
  13. package/dist/core/opengoat/application/opengoat.service.helpers.js.map +1 -1
  14. package/dist/core/opengoat/application/opengoat.service.js +310 -47
  15. package/dist/core/opengoat/application/opengoat.service.js.map +1 -1
  16. package/dist/core/orchestration/application/orchestration.service.js +6 -1
  17. package/dist/core/orchestration/application/orchestration.service.js.map +1 -1
  18. package/dist/core/ports/file-system.port.d.ts +3 -0
  19. package/dist/core/providers/application/provider.service.d.ts +3 -0
  20. package/dist/core/providers/application/provider.service.js +126 -0
  21. package/dist/core/providers/application/provider.service.js.map +1 -1
  22. package/dist/core/providers/cli-provider.d.ts +1 -0
  23. package/dist/core/providers/cli-provider.js +11 -8
  24. package/dist/core/providers/cli-provider.js.map +1 -1
  25. package/dist/core/providers/openclaw-gateway-rpc.js +70 -17
  26. package/dist/core/providers/openclaw-gateway-rpc.js.map +1 -1
  27. package/dist/core/providers/providers/codex/provider.d.ts +1 -0
  28. package/dist/core/providers/providers/codex/provider.js +24 -0
  29. package/dist/core/providers/providers/codex/provider.js.map +1 -1
  30. package/dist/core/providers/providers/copilot-cli/index.d.ts +4 -0
  31. package/dist/core/providers/providers/copilot-cli/index.js +37 -0
  32. package/dist/core/providers/providers/copilot-cli/index.js.map +1 -0
  33. package/dist/core/providers/providers/copilot-cli/provider.d.ts +6 -0
  34. package/dist/core/providers/providers/copilot-cli/provider.js +39 -0
  35. package/dist/core/providers/providers/copilot-cli/provider.js.map +1 -0
  36. package/dist/core/providers/providers/registry.js +2 -0
  37. package/dist/core/providers/providers/registry.js.map +1 -1
  38. package/dist/core/providers/types.d.ts +1 -0
  39. package/dist/core/templates/assets/ceo/BOOTSTRAP.md +3 -3
  40. package/dist/core/templates/assets/ceo/ROLE.md +4 -4
  41. package/dist/core/templates/assets/skills/og-board-manager/SKILL.md +3 -6
  42. package/dist/platform/node/node-file-system.d.ts +3 -0
  43. package/dist/platform/node/node-file-system.js +30 -1
  44. package/dist/platform/node/node-file-system.js.map +1 -1
  45. package/package.json +1 -1
@@ -10,15 +10,21 @@ import { ProviderCommandNotFoundError, createDefaultProviderRegistry, } from "..
10
10
  import { dirname, resolve as resolvePath } from "node:path";
11
11
  import { SessionService, } from "../../sessions/index.js";
12
12
  import { SkillService, } from "../../skills/index.js";
13
- import { assertAgentExists, buildBlockedTaskMessage, buildInactiveAgentsMessage, buildNotificationSessionRef, buildPendingTaskMessage, buildReporteeStats, buildTodoTaskMessage, collectAllReportees, containsAgentNotFoundMessage, containsAlreadyExistsMessage, extractManagedSkillsDir, extractOpenClawAgents, isSpawnPermissionOrMissing, pathIsWithin, pathMatches, prepareOpenClawCommandEnv, resolveInactiveAgentNotificationTarget, resolveInactiveMinutes, resolveMaxParallelFlows, toErrorMessage, } from "./opengoat.service.helpers.js";
13
+ import { assertAgentExists, buildBlockedTaskMessage, buildDoingTaskMessage, buildInactiveAgentsMessage, buildNotificationSessionRef, buildPendingTaskMessage, buildReporteeStats, buildTopDownTaskDelegationMessage, buildTodoTaskMessage, collectAllReportees, containsAgentNotFoundMessage, containsAlreadyExistsMessage, extractManagedSkillsDir, extractOpenClawAgents, isSpawnPermissionOrMissing, pathIsWithin, pathMatches, prepareOpenClawCommandEnv, resolveInProgressTimeoutMinutes, resolveBottomUpTaskDelegationStrategy, resolveTopDownTaskDelegationStrategy, resolveMaxParallelFlows, toErrorMessage, } from "./opengoat.service.helpers.js";
14
14
  const OPENCLAW_PROVIDER_ID = "openclaw";
15
15
  const OPENCLAW_DEFAULT_AGENT_ID = "main";
16
16
  const OPENCLAW_AGENT_SANDBOX_MODE = "off";
17
17
  const OPENCLAW_AGENT_TOOLS_ALLOW_ALL_JSON = "[\"*\"]";
18
+ const OPENCLAW_AGENT_SKIP_BOOTSTRAP = true;
18
19
  const OPENCLAW_OPENGOAT_PLUGIN_ID = "openclaw-plugin";
19
20
  const OPENCLAW_OPENGOAT_PLUGIN_ROOT_ID = "opengoat-plugin";
20
21
  const OPENCLAW_OPENGOAT_PLUGIN_LEGACY_PACK_ID = "openclaw-plugin-pack";
21
22
  const OPENCLAW_OPENGOAT_PLUGIN_FALLBACK_ID = "workspace";
23
+ const NOTIFICATION_SESSION_COMPACTION_COMMAND = [
24
+ "/compact",
25
+ "Keep only the last 3 notification exchanges plus active task ids, statuses, blockers, and explicit next actions.",
26
+ "Remove redundant older notification chatter.",
27
+ ].join(" ");
22
28
  export class OpenGoatService {
23
29
  fileSystem;
24
30
  pathPort;
@@ -205,23 +211,31 @@ export class OpenGoatService {
205
211
  warnings.push(`OpenClaw role skill assignment sync for "ceo" failed: ${toErrorMessage(error)}`);
206
212
  }
207
213
  let openClawAgentEntriesById;
214
+ let openClawInventoryAvailable = false;
208
215
  try {
209
216
  openClawAgentEntriesById = new Map((await this.listOpenClawAgents(paths)).map((entry) => [entry.id, entry]));
217
+ openClawInventoryAvailable = true;
210
218
  }
211
219
  catch (error) {
212
220
  warnings.push(`OpenClaw startup inventory check failed: ${toErrorMessage(error)}`);
213
221
  }
214
222
  try {
215
223
  localAgents = await this.agentService.listAgents(paths);
216
- for (const agent of localAgents) {
217
- const sync = await this.syncOpenClawAgentRegistration(paths, {
218
- descriptor: agent,
219
- existingEntry: openClawAgentEntriesById?.get(agent.id),
220
- });
221
- warnings.push(...sync.warnings);
222
- if (agent.id === DEFAULT_AGENT_ID) {
223
- ceoSynced = sync.synced;
224
- ceoSyncCode = sync.code;
224
+ if (!openClawInventoryAvailable) {
225
+ warnings.push("OpenClaw startup registration sync skipped because agent inventory is unavailable.");
226
+ ceoSynced = localAgents.some((agent) => agent.id === DEFAULT_AGENT_ID);
227
+ }
228
+ else {
229
+ for (const agent of localAgents) {
230
+ const sync = await this.syncOpenClawAgentRegistration(paths, {
231
+ descriptor: agent,
232
+ existingEntry: openClawAgentEntriesById?.get(agent.id),
233
+ });
234
+ warnings.push(...sync.warnings);
235
+ if (agent.id === DEFAULT_AGENT_ID) {
236
+ ceoSynced = sync.synced;
237
+ ceoSyncCode = sync.code;
238
+ }
225
239
  }
226
240
  }
227
241
  }
@@ -249,6 +263,12 @@ export class OpenGoatService {
249
263
  catch (error) {
250
264
  warnings.push(`OpenGoat workspace command shim sync failed: ${toErrorMessage(error)}`);
251
265
  }
266
+ try {
267
+ await this.agentService.syncWorkspaceReporteeLinks(paths);
268
+ }
269
+ catch (error) {
270
+ warnings.push(`OpenGoat workspace reportees sync failed: ${toErrorMessage(error)}`);
271
+ }
252
272
  return {
253
273
  ceoSyncCode,
254
274
  ceoSynced,
@@ -308,24 +328,35 @@ export class OpenGoatService {
308
328
  throw new Error(`OpenClaw agent location sync failed for "${created.agent.id}". ${toErrorMessage(error)}`);
309
329
  }
310
330
  await this.syncOpenClawAgentExecutionPolicies(paths, [created.agent.id]);
331
+ if (!created.alreadyExisted) {
332
+ try {
333
+ const workspaceBootstrap = await this.agentService.ensureAgentWorkspaceBootstrap(paths, {
334
+ agentId: created.agent.id,
335
+ displayName: created.agent.displayName,
336
+ role: options.role?.trim() ?? "",
337
+ }, {
338
+ syncBootstrapMarkdown: false,
339
+ });
340
+ created.createdPaths.push(...workspaceBootstrap.createdPaths);
341
+ created.skippedPaths.push(...workspaceBootstrap.skippedPaths);
342
+ created.skippedPaths.push(...workspaceBootstrap.removedPaths);
343
+ }
344
+ catch (error) {
345
+ await this.agentService.removeAgent(paths, created.agent.id);
346
+ throw new Error(`Failed to update workspace bootstrap for "${created.agent.id}". ${toErrorMessage(error)}`);
347
+ }
348
+ }
311
349
  try {
312
- const workspaceBootstrap = await this.agentService.ensureAgentWorkspaceBootstrap(paths, {
313
- agentId: created.agent.id,
314
- displayName: created.agent.displayName,
315
- role: options.role?.trim() ??
316
- (created.alreadyExisted ? created.agent.role : ""),
317
- }, {
318
- syncBootstrapMarkdown: false,
319
- });
320
- created.createdPaths.push(...workspaceBootstrap.createdPaths);
321
- created.skippedPaths.push(...workspaceBootstrap.skippedPaths);
322
- created.skippedPaths.push(...workspaceBootstrap.removedPaths);
350
+ const workspaceReporteesSync = await this.agentService.syncWorkspaceReporteeLinks(paths);
351
+ created.createdPaths.push(...workspaceReporteesSync.createdPaths);
352
+ created.skippedPaths.push(...workspaceReporteesSync.skippedPaths);
353
+ created.skippedPaths.push(...workspaceReporteesSync.removedPaths);
323
354
  }
324
355
  catch (error) {
325
356
  if (!created.alreadyExisted) {
326
357
  await this.agentService.removeAgent(paths, created.agent.id);
327
358
  }
328
- throw new Error(`Failed to update workspace bootstrap for "${created.agent.id}". ${toErrorMessage(error)}`);
359
+ throw new Error(`Failed to sync reportees workspace links for "${created.agent.id}". ${toErrorMessage(error)}`);
329
360
  }
330
361
  return {
331
362
  ...created,
@@ -354,6 +385,7 @@ export class OpenGoatService {
354
385
  throw new Error(`OpenClaw agent deletion failed for "${agentId}" (exit ${runtimeSync.code}). ${runtimeSync.stderr.trim() || runtimeSync.stdout.trim() || ""}`.trim());
355
386
  }
356
387
  const removed = await this.agentService.removeAgent(paths, agentId);
388
+ await this.agentService.syncWorkspaceReporteeLinks(paths);
357
389
  return {
358
390
  ...removed,
359
391
  runtimeSync: {
@@ -378,6 +410,7 @@ export class OpenGoatService {
378
410
  if (updated.reportsTo) {
379
411
  await this.syncOpenClawRoleSkills(paths, updated.reportsTo);
380
412
  }
413
+ await this.agentService.syncWorkspaceReporteeLinks(paths);
381
414
  return updated;
382
415
  }
383
416
  async listAgents() {
@@ -555,18 +588,23 @@ export class OpenGoatService {
555
588
  const paths = this.pathsProvider.getPaths();
556
589
  const ranAt = this.resolveNowIso();
557
590
  const manifests = await this.agentManifestService.listManifests(paths);
558
- const inactiveMinutes = resolveInactiveMinutes(options.inactiveMinutes);
559
- const notificationTarget = resolveInactiveAgentNotificationTarget(options.notificationTarget);
560
- const notifyInactiveAgents = options.notifyInactiveAgents ?? true;
591
+ const inProgressMinutes = resolveInProgressTimeoutMinutes(options.inProgressMinutes);
592
+ const topDownStrategy = resolveTopDownTaskDelegationStrategy(options);
593
+ const bottomUpStrategy = resolveBottomUpTaskDelegationStrategy(options);
561
594
  const maxParallelFlows = resolveMaxParallelFlows(options.maxParallelFlows);
562
- const inactiveCandidates = notifyInactiveAgents
563
- ? await this.collectInactiveAgents(paths, manifests, inactiveMinutes, notificationTarget)
595
+ const inactiveCandidates = bottomUpStrategy.enabled
596
+ ? await this.collectInactiveAgents(paths, manifests, bottomUpStrategy.inactiveMinutes, bottomUpStrategy.notificationTarget)
564
597
  : [];
565
598
  const tasks = await this.boardService.listTasks(paths, { limit: 10_000 });
566
- const pendingTaskIds = new Set(await this.boardService.listPendingTaskIdsOlderThan(paths, inactiveMinutes));
567
- const taskStatusDispatch = await this.dispatchTaskStatusAutomations(paths, tasks, manifests, pendingTaskIds, inactiveMinutes, ranAt, maxParallelFlows);
568
- const inactiveDispatches = await this.dispatchInactiveAgentAutomations(paths, inactiveCandidates, inactiveMinutes, ranAt, maxParallelFlows);
599
+ const topDownDispatches = topDownStrategy.enabled
600
+ ? await this.dispatchTopDownTaskDelegationAutomations(paths, tasks, manifests, topDownStrategy.openTasksThreshold, ranAt, maxParallelFlows)
601
+ : [];
602
+ const pendingTaskIds = new Set(await this.boardService.listPendingTaskIdsOlderThan(paths, bottomUpStrategy.inactiveMinutes));
603
+ const doingTaskIds = new Set(await this.boardService.listDoingTaskIdsOlderThan(paths, inProgressMinutes));
604
+ const taskStatusDispatch = await this.dispatchTaskStatusAutomations(paths, tasks, manifests, doingTaskIds, pendingTaskIds, inProgressMinutes, bottomUpStrategy.inactiveMinutes, ranAt, maxParallelFlows);
605
+ const inactiveDispatches = await this.dispatchInactiveAgentAutomations(paths, inactiveCandidates, bottomUpStrategy.inactiveMinutes, ranAt, maxParallelFlows);
569
606
  const dispatches = [
607
+ ...topDownDispatches,
570
608
  ...taskStatusDispatch.dispatches,
571
609
  ...inactiveDispatches,
572
610
  ];
@@ -575,6 +613,7 @@ export class OpenGoatService {
575
613
  ranAt,
576
614
  scannedTasks: tasks.length,
577
615
  todoTasks: taskStatusDispatch.todoTasks,
616
+ doingTasks: taskStatusDispatch.doingTasks,
578
617
  blockedTasks: taskStatusDispatch.blockedTasks,
579
618
  inactiveAgents: inactiveCandidates.length,
580
619
  sent: dispatches.length - failed,
@@ -582,13 +621,71 @@ export class OpenGoatService {
582
621
  dispatches,
583
622
  };
584
623
  }
585
- async dispatchTaskStatusAutomations(paths, tasks, manifests, pendingTaskIds, pendingMinutes, notificationTimestamp, maxParallelFlows) {
624
+ async dispatchTopDownTaskDelegationAutomations(paths, tasks, manifests, openTasksThreshold, notificationTimestamp, maxParallelFlows = 1) {
625
+ const openTasks = tasks
626
+ .filter((task) => isTopDownOpenTaskStatus(task.status))
627
+ .sort((left, right) => {
628
+ const leftTimestamp = Date.parse(left.updatedAt);
629
+ const rightTimestamp = Date.parse(right.updatedAt);
630
+ if (Number.isFinite(leftTimestamp) && Number.isFinite(rightTimestamp)) {
631
+ return rightTimestamp - leftTimestamp;
632
+ }
633
+ if (Number.isFinite(rightTimestamp)) {
634
+ return 1;
635
+ }
636
+ if (Number.isFinite(leftTimestamp)) {
637
+ return -1;
638
+ }
639
+ return right.taskId.localeCompare(left.taskId);
640
+ });
641
+ if (openTasks.length > openTasksThreshold) {
642
+ return [];
643
+ }
644
+ const managerAgents = manifests.filter((manifest) => manifest.metadata.type === "manager").length;
645
+ const ceoDirectReportees = manifests.filter((manifest) => normalizeAgentId(manifest.metadata.reportsTo ?? "") === DEFAULT_AGENT_ID).length;
646
+ const sessionRef = buildNotificationSessionRef(DEFAULT_AGENT_ID);
647
+ const message = buildTopDownTaskDelegationMessage({
648
+ openTasksThreshold,
649
+ openTasksCount: openTasks.length,
650
+ totalAgents: manifests.length,
651
+ managerAgents,
652
+ ceoDirectReportees,
653
+ openTasks: openTasks.map((task) => ({
654
+ taskId: task.taskId,
655
+ title: task.title,
656
+ status: task.status,
657
+ assignedTo: task.assignedTo,
658
+ })),
659
+ notificationTimestamp,
660
+ });
661
+ const requests = [
662
+ {
663
+ targetAgentId: DEFAULT_AGENT_ID,
664
+ sessionRef,
665
+ message,
666
+ },
667
+ ];
668
+ return runWithConcurrencyByKey(requests, maxParallelFlows, (request) => request.targetAgentId, async (request) => {
669
+ const result = await this.dispatchAutomationMessage(paths, request.targetAgentId, request.sessionRef, request.message);
670
+ return {
671
+ kind: "topdown",
672
+ targetAgentId: request.targetAgentId,
673
+ sessionRef: request.sessionRef,
674
+ message: request.message,
675
+ ok: result.ok,
676
+ error: result.error,
677
+ };
678
+ });
679
+ }
680
+ async dispatchTaskStatusAutomations(paths, tasks, manifests, doingTaskIds, pendingTaskIds, doingMinutes, pendingMinutes, notificationTimestamp, maxParallelFlows) {
586
681
  const manifestsById = new Map(manifests.map((manifest) => [manifest.agentId, manifest]));
587
682
  const requests = [];
588
683
  let todoTasks = 0;
684
+ let doingTasks = 0;
589
685
  let pendingTasks = 0;
590
686
  let blockedTasks = 0;
591
- for (const task of tasks) {
687
+ const orderedTasks = [...tasks].sort(compareTaskCreatedAtAscending);
688
+ for (const task of orderedTasks) {
592
689
  if (task.status === "todo") {
593
690
  todoTasks += 1;
594
691
  const targetAgentId = task.assignedTo;
@@ -606,6 +703,27 @@ export class OpenGoatService {
606
703
  });
607
704
  continue;
608
705
  }
706
+ if (task.status === "doing") {
707
+ if (!doingTaskIds.has(task.taskId)) {
708
+ continue;
709
+ }
710
+ doingTasks += 1;
711
+ const targetAgentId = task.assignedTo;
712
+ const sessionRef = buildNotificationSessionRef(targetAgentId);
713
+ const message = buildDoingTaskMessage({
714
+ task,
715
+ doingMinutes,
716
+ notificationTimestamp,
717
+ });
718
+ requests.push({
719
+ kind: "doing",
720
+ targetAgentId,
721
+ sessionRef,
722
+ taskId: task.taskId,
723
+ message,
724
+ });
725
+ continue;
726
+ }
609
727
  if (task.status === "pending") {
610
728
  if (!pendingTaskIds.has(task.taskId)) {
611
729
  continue;
@@ -658,9 +776,17 @@ export class OpenGoatService {
658
776
  error: result.error,
659
777
  };
660
778
  });
779
+ const notifiedDoingTaskIds = dedupeStrings(dispatches
780
+ .filter((dispatch) => dispatch.kind === "doing" && dispatch.ok)
781
+ .map((dispatch) => dispatch.taskId)
782
+ .filter((taskId) => typeof taskId === "string"));
783
+ await Promise.all(notifiedDoingTaskIds.map(async (taskId) => {
784
+ await this.boardService.resetTaskStatusTimeout(paths, taskId, "doing");
785
+ }));
661
786
  return {
662
787
  dispatches,
663
788
  todoTasks,
789
+ doingTasks,
664
790
  pendingTasks,
665
791
  blockedTasks,
666
792
  };
@@ -796,14 +922,36 @@ export class OpenGoatService {
796
922
  return this.pathsProvider.getPaths();
797
923
  }
798
924
  async dispatchAutomationMessage(paths, agentId, sessionRef, message, options = {}) {
925
+ const invoke = async (nextMessage) => this.runAgent(agentId, {
926
+ message: nextMessage,
927
+ sessionRef,
928
+ disableSession: options.disableSession ?? false,
929
+ cwd: options.cwd,
930
+ env: process.env,
931
+ });
799
932
  try {
800
- const result = await this.orchestrationService.runAgent(paths, agentId, {
801
- message,
802
- sessionRef,
803
- disableSession: options.disableSession ?? false,
804
- cwd: options.cwd,
805
- env: process.env,
806
- });
933
+ let result = await invoke(message);
934
+ if (!isNotificationSessionRef(sessionRef) ||
935
+ options.disableSession === true ||
936
+ !isContextOverflowError(result.stderr, result.stdout)) {
937
+ if (result.code !== 0) {
938
+ return {
939
+ ok: false,
940
+ error: (result.stderr ||
941
+ result.stdout ||
942
+ `Runtime exited with code ${result.code}.`).trim(),
943
+ };
944
+ }
945
+ return { ok: true };
946
+ }
947
+ const binding = await this.providerService.getAgentProvider(paths, agentId);
948
+ if (binding.providerId === OPENCLAW_PROVIDER_ID &&
949
+ !message.trim().startsWith("/")) {
950
+ const compact = await invoke(NOTIFICATION_SESSION_COMPACTION_COMMAND);
951
+ if (compact.code === 0) {
952
+ result = await invoke(message);
953
+ }
954
+ }
807
955
  if (result.code !== 0) {
808
956
  return {
809
957
  ok: false,
@@ -1162,6 +1310,12 @@ export class OpenGoatService {
1162
1310
  warnings.push(`OpenClaw tools policy sync failed for "${agentId}" (code ${toolsSet.code}). ${toolsSet.stderr.trim() || toolsSet.stdout.trim() || ""}`.trim());
1163
1311
  }
1164
1312
  }
1313
+ if (readAgentSkipBootstrap(entry) !== OPENCLAW_AGENT_SKIP_BOOTSTRAP) {
1314
+ const bootstrapSet = await this.runOpenClaw(["config", "set", `agents.list[${index}].skipBootstrap`, "true"], { env });
1315
+ if (bootstrapSet.code !== 0) {
1316
+ warnings.push(`OpenClaw bootstrap policy sync failed for "${agentId}" (code ${bootstrapSet.code}). ${bootstrapSet.stderr.trim() || bootstrapSet.stdout.trim() || ""}`.trim());
1317
+ }
1318
+ }
1165
1319
  }
1166
1320
  return warnings;
1167
1321
  }
@@ -1376,6 +1530,22 @@ function hasAgentToolsAllowAll(entry) {
1376
1530
  }
1377
1531
  return allow.some((value) => typeof value === "string" && value.trim() === "*");
1378
1532
  }
1533
+ function readAgentSkipBootstrap(entry) {
1534
+ const value = entry.skipBootstrap;
1535
+ if (typeof value === "boolean") {
1536
+ return value;
1537
+ }
1538
+ if (typeof value === "string") {
1539
+ const normalized = value.trim().toLowerCase();
1540
+ if (normalized === "true") {
1541
+ return true;
1542
+ }
1543
+ if (normalized === "false") {
1544
+ return false;
1545
+ }
1546
+ }
1547
+ return undefined;
1548
+ }
1379
1549
  function readStringArray(value) {
1380
1550
  if (!Array.isArray(value)) {
1381
1551
  return [];
@@ -1426,16 +1596,38 @@ async function runWithConcurrencyByKey(items, rawConcurrency, resolveKey, worker
1426
1596
  });
1427
1597
  return results;
1428
1598
  }
1599
+ function compareTaskCreatedAtAscending(left, right) {
1600
+ const leftTimestamp = Date.parse(left.createdAt);
1601
+ const rightTimestamp = Date.parse(right.createdAt);
1602
+ if (Number.isFinite(leftTimestamp) && Number.isFinite(rightTimestamp)) {
1603
+ const timestampDiff = leftTimestamp - rightTimestamp;
1604
+ if (timestampDiff !== 0) {
1605
+ return timestampDiff;
1606
+ }
1607
+ return left.taskId.localeCompare(right.taskId);
1608
+ }
1609
+ if (Number.isFinite(leftTimestamp)) {
1610
+ return -1;
1611
+ }
1612
+ if (Number.isFinite(rightTimestamp)) {
1613
+ return 1;
1614
+ }
1615
+ return left.taskId.localeCompare(right.taskId);
1616
+ }
1429
1617
  function parseLooseJson(raw) {
1430
1618
  const trimmed = raw.trim();
1431
1619
  if (!trimmed) {
1432
1620
  return undefined;
1433
1621
  }
1434
- try {
1435
- return JSON.parse(trimmed);
1622
+ const exact = tryParseJson(trimmed);
1623
+ if (exact !== undefined) {
1624
+ return exact;
1436
1625
  }
1437
- catch {
1438
- // continue
1626
+ for (const line of trimmed.split(/\r?\n/)) {
1627
+ const parsedLine = tryParseJson(line.trim());
1628
+ if (parsedLine !== undefined) {
1629
+ return parsedLine;
1630
+ }
1439
1631
  }
1440
1632
  const starts = dedupeNumbers([
1441
1633
  trimmed.indexOf("{"),
@@ -1448,11 +1640,69 @@ function parseLooseJson(raw) {
1448
1640
  if (!candidate) {
1449
1641
  continue;
1450
1642
  }
1451
- try {
1452
- return JSON.parse(candidate);
1643
+ const parsedCandidate = tryParseJson(candidate);
1644
+ if (parsedCandidate !== undefined) {
1645
+ return parsedCandidate;
1453
1646
  }
1454
- catch {
1455
- // keep trying candidates
1647
+ const balancedCandidate = extractBalancedJsonCandidate(trimmed, startIndex);
1648
+ if (!balancedCandidate) {
1649
+ continue;
1650
+ }
1651
+ const parsedBalancedCandidate = tryParseJson(balancedCandidate);
1652
+ if (parsedBalancedCandidate !== undefined) {
1653
+ return parsedBalancedCandidate;
1654
+ }
1655
+ }
1656
+ return undefined;
1657
+ }
1658
+ function tryParseJson(raw) {
1659
+ if (!raw) {
1660
+ return undefined;
1661
+ }
1662
+ try {
1663
+ return JSON.parse(raw);
1664
+ }
1665
+ catch {
1666
+ return undefined;
1667
+ }
1668
+ }
1669
+ function extractBalancedJsonCandidate(raw, startIndex) {
1670
+ const opening = raw[startIndex];
1671
+ if (opening !== "{" && opening !== "[") {
1672
+ return undefined;
1673
+ }
1674
+ const closing = opening === "{" ? "}" : "]";
1675
+ let depth = 0;
1676
+ let inString = false;
1677
+ let escaping = false;
1678
+ for (let index = startIndex; index < raw.length; index += 1) {
1679
+ const char = raw[index];
1680
+ if (inString) {
1681
+ if (escaping) {
1682
+ escaping = false;
1683
+ }
1684
+ else if (char === "\\") {
1685
+ escaping = true;
1686
+ }
1687
+ else if (char === "\"") {
1688
+ inString = false;
1689
+ }
1690
+ continue;
1691
+ }
1692
+ if (char === "\"") {
1693
+ inString = true;
1694
+ continue;
1695
+ }
1696
+ if (char === opening) {
1697
+ depth += 1;
1698
+ continue;
1699
+ }
1700
+ if (char !== closing) {
1701
+ continue;
1702
+ }
1703
+ depth -= 1;
1704
+ if (depth === 0) {
1705
+ return raw.slice(startIndex, index + 1).trim();
1456
1706
  }
1457
1707
  }
1458
1708
  return undefined;
@@ -1526,4 +1776,17 @@ function isOpenGoatPluginId(pluginId) {
1526
1776
  function isPluginNotFoundMessage(message) {
1527
1777
  return message.toLowerCase().includes("plugin not found");
1528
1778
  }
1779
+ function isNotificationSessionRef(sessionRef) {
1780
+ return sessionRef.trim().toLowerCase().endsWith("_notifications");
1781
+ }
1782
+ function isContextOverflowError(stderr, stdout) {
1783
+ const combined = `${stderr}\n${stdout}`.toLowerCase();
1784
+ return (combined.includes("context overflow") ||
1785
+ combined.includes("prompt too large") ||
1786
+ combined.includes("context length"));
1787
+ }
1788
+ function isTopDownOpenTaskStatus(status) {
1789
+ const normalized = status.trim().toLowerCase();
1790
+ return normalized !== "done" && normalized !== "blocked";
1791
+ }
1529
1792
  //# sourceMappingURL=opengoat.service.js.map