@renseiai/agentfactory 0.8.13 → 0.8.14

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 (49) hide show
  1. package/dist/src/orchestrator/completion-contracts.d.ts +89 -0
  2. package/dist/src/orchestrator/completion-contracts.d.ts.map +1 -0
  3. package/dist/src/orchestrator/completion-contracts.js +228 -0
  4. package/dist/src/orchestrator/completion-contracts.test.d.ts +2 -0
  5. package/dist/src/orchestrator/completion-contracts.test.d.ts.map +1 -0
  6. package/dist/src/orchestrator/completion-contracts.test.js +195 -0
  7. package/dist/src/orchestrator/index.d.ts +4 -0
  8. package/dist/src/orchestrator/index.d.ts.map +1 -1
  9. package/dist/src/orchestrator/index.js +3 -0
  10. package/dist/src/orchestrator/orchestrator.d.ts +32 -0
  11. package/dist/src/orchestrator/orchestrator.d.ts.map +1 -1
  12. package/dist/src/orchestrator/orchestrator.js +157 -26
  13. package/dist/src/orchestrator/session-backstop.d.ts +67 -0
  14. package/dist/src/orchestrator/session-backstop.d.ts.map +1 -0
  15. package/dist/src/orchestrator/session-backstop.js +394 -0
  16. package/dist/src/orchestrator/session-backstop.test.d.ts +2 -0
  17. package/dist/src/orchestrator/session-backstop.test.d.ts.map +1 -0
  18. package/dist/src/orchestrator/session-backstop.test.js +245 -0
  19. package/dist/src/orchestrator/worktree-checks.test.d.ts +2 -0
  20. package/dist/src/orchestrator/worktree-checks.test.d.ts.map +1 -0
  21. package/dist/src/orchestrator/worktree-checks.test.js +159 -0
  22. package/dist/src/providers/a2a-provider.d.ts +4 -0
  23. package/dist/src/providers/a2a-provider.d.ts.map +1 -1
  24. package/dist/src/providers/a2a-provider.js +4 -0
  25. package/dist/src/providers/amp-provider.d.ts +4 -0
  26. package/dist/src/providers/amp-provider.d.ts.map +1 -1
  27. package/dist/src/providers/amp-provider.js +4 -0
  28. package/dist/src/providers/claude-provider.d.ts +4 -0
  29. package/dist/src/providers/claude-provider.d.ts.map +1 -1
  30. package/dist/src/providers/claude-provider.js +6 -0
  31. package/dist/src/providers/codex-provider.d.ts +4 -0
  32. package/dist/src/providers/codex-provider.d.ts.map +1 -1
  33. package/dist/src/providers/codex-provider.js +4 -0
  34. package/dist/src/providers/index.d.ts +1 -1
  35. package/dist/src/providers/index.d.ts.map +1 -1
  36. package/dist/src/providers/spring-ai-provider.d.ts +4 -0
  37. package/dist/src/providers/spring-ai-provider.d.ts.map +1 -1
  38. package/dist/src/providers/spring-ai-provider.js +4 -0
  39. package/dist/src/providers/types.d.ts +22 -0
  40. package/dist/src/providers/types.d.ts.map +1 -1
  41. package/dist/src/templates/types.d.ts +3 -0
  42. package/dist/src/templates/types.d.ts.map +1 -1
  43. package/dist/src/templates/types.js +2 -0
  44. package/dist/src/tools/index.d.ts +1 -0
  45. package/dist/src/tools/index.d.ts.map +1 -1
  46. package/dist/src/tools/registry.d.ts +6 -1
  47. package/dist/src/tools/registry.d.ts.map +1 -1
  48. package/dist/src/tools/registry.js +5 -1
  49. package/package.json +2 -2
@@ -16,6 +16,7 @@ import { createSessionLogger } from './session-logger.js';
16
16
  import { ContextManager } from './context-manager.js';
17
17
  import { isSessionLoggingEnabled, getLogAnalysisConfig } from './log-config.js';
18
18
  import { parseWorkResult } from './parse-work-result.js';
19
+ import { runBackstop, formatBackstopComment } from './session-backstop.js';
19
20
  import { createActivityEmitter } from './activity-emitter.js';
20
21
  import { createApiActivityEmitter } from './api-activity-emitter.js';
21
22
  import { createLogger } from '../logger.js';
@@ -303,7 +304,7 @@ function extractToolNameFromError(error) {
303
304
  * @param worktreePath - Path to the git worktree
304
305
  * @returns Check result with reason if incomplete work is found
305
306
  */
306
- function checkForIncompleteWork(worktreePath) {
307
+ export function checkForIncompleteWork(worktreePath) {
307
308
  try {
308
309
  // Check for uncommitted changes (staged or unstaged)
309
310
  const statusOutput = execSync('git status --porcelain', {
@@ -358,15 +359,12 @@ function checkForIncompleteWork(worktreePath) {
358
359
  encoding: 'utf-8',
359
360
  timeout: 10000,
360
361
  }).trim();
361
- try {
362
- execSync(`git ls-remote --heads origin ${currentBranch}`, {
363
- cwd: worktreePath,
364
- encoding: 'utf-8',
365
- timeout: 10000,
366
- });
367
- // Remote branch exists, no issue
368
- }
369
- catch {
362
+ const remoteRef = execSync(`git ls-remote --heads origin ${currentBranch}`, {
363
+ cwd: worktreePath,
364
+ encoding: 'utf-8',
365
+ timeout: 10000,
366
+ }).trim();
367
+ if (remoteRef.length === 0) {
370
368
  // Remote branch doesn't exist - branch never pushed
371
369
  return {
372
370
  hasIncompleteWork: true,
@@ -391,7 +389,7 @@ function checkForIncompleteWork(worktreePath) {
391
389
  };
392
390
  }
393
391
  }
394
- function checkForPushedWorkWithoutPR(worktreePath) {
392
+ export function checkForPushedWorkWithoutPR(worktreePath) {
395
393
  try {
396
394
  const currentBranch = execSync('git branch --show-current', {
397
395
  cwd: worktreePath,
@@ -840,6 +838,8 @@ export class AgentOrchestrator {
840
838
  // Session loggers per agent for verbose analysis logging
841
839
  sessionLoggers = new Map();
842
840
  contextManagers = new Map();
841
+ // Session output flags for completion contract validation (keyed by issueId)
842
+ sessionOutputFlags = new Map();
843
843
  // Template registry for configurable workflow prompts
844
844
  templateRegistry;
845
845
  // Allowlisted project names from .agentfactory/config.yaml
@@ -1287,6 +1287,17 @@ export class AgentOrchestrator {
1287
1287
  return false;
1288
1288
  }
1289
1289
  }
1290
+ // SAFETY GUARD 4: Never destroy an explicitly preserved worktree
1291
+ // Preserved worktrees contain work that the orchestrator decided to keep
1292
+ // for manual recovery. The next agent should reuse the branch (which still
1293
+ // exists after worktree removal) but NOT destroy the preserved directory.
1294
+ const preservedMarker = resolve(conflictPath, '.agent', 'preserved.json');
1295
+ if (existsSync(preservedMarker)) {
1296
+ console.warn(`SAFETY: Refusing to clean up ${conflictPath} — it is a preserved worktree ` +
1297
+ `with incomplete work. The agent that ran here exited without creating a PR. ` +
1298
+ `Recover manually: cd into the worktree, commit, push, and create a PR.`);
1299
+ return false;
1300
+ }
1290
1301
  // Check if the agent in the conflicting worktree is still alive
1291
1302
  const recoveryInfo = checkRecovery(conflictPath, {
1292
1303
  heartbeatTimeoutMs: getHeartbeatTimeoutFromEnv(),
@@ -1899,6 +1910,7 @@ ORCHESTRATOR_INSTALL=1 exec pnpm add "$@"
1899
1910
  buildCommand: perProject?.buildCommand ?? this.buildCommand,
1900
1911
  testCommand: perProject?.testCommand ?? this.testCommand,
1901
1912
  validateCommand: perProject?.validateCommand ?? this.validateCommand,
1913
+ agentBugBacklog: process.env.AGENT_BUG_BACKLOG || undefined,
1902
1914
  };
1903
1915
  const rendered = this.templateRegistry.renderPrompt(workType, context);
1904
1916
  prompt = rendered ?? generatePromptForWorkType(identifier, workType);
@@ -2080,7 +2092,7 @@ ORCHESTRATOR_INSTALL=1 exec pnpm add "$@"
2080
2092
  }
2081
2093
  log.info('Starting agent via provider', { provider: spawnProviderName, source: providerSource, cwd: worktreePath ?? 'repo-root', workType, promptPreview: prompt.substring(0, 50) });
2082
2094
  // Create in-process tool servers from registered plugins
2083
- const mcpServers = spawnProviderName === 'claude'
2095
+ const toolServers = spawnProviderName === 'claude'
2084
2096
  ? this.toolRegistry.createServers({ env, cwd: worktreePath ?? process.cwd() })
2085
2097
  : undefined;
2086
2098
  // Coordinators need significantly more turns than standard agents
@@ -2096,7 +2108,8 @@ ORCHESTRATOR_INSTALL=1 exec pnpm add "$@"
2096
2108
  abortController,
2097
2109
  autonomous: true,
2098
2110
  sandboxEnabled: this.config.sandboxEnabled,
2099
- mcpServers,
2111
+ mcpServers: toolServers?.servers,
2112
+ mcpToolNames: toolServers?.toolNames,
2100
2113
  maxTurns,
2101
2114
  onProcessSpawned: (pid) => {
2102
2115
  agent.pid = pid;
@@ -2213,6 +2226,43 @@ ORCHESTRATOR_INSTALL=1 exec pnpm add "$@"
2213
2226
  }
2214
2227
  }
2215
2228
  }
2229
+ // --- Session Backstop: Validate completion contract and recover missing outputs ---
2230
+ if (agent.status === 'completed') {
2231
+ const outputFlags = this.sessionOutputFlags.get(issueId);
2232
+ const backstopCtx = {
2233
+ agent,
2234
+ commentPosted: outputFlags?.commentPosted ?? false,
2235
+ issueUpdated: outputFlags?.issueUpdated ?? false,
2236
+ subIssuesCreated: outputFlags?.subIssuesCreated ?? false,
2237
+ };
2238
+ const backstopResult = runBackstop(backstopCtx);
2239
+ if (backstopResult.backstop.actions.length > 0) {
2240
+ log?.info('Session backstop ran', {
2241
+ actions: backstopResult.backstop.actions.map(a => `${a.field}:${a.success ? 'ok' : 'fail'}`),
2242
+ fullyRecovered: backstopResult.backstop.fullyRecovered,
2243
+ remainingGaps: backstopResult.backstop.remainingGaps,
2244
+ });
2245
+ // Post backstop diagnostic comment if there were actions taken or gaps remaining
2246
+ const backstopComment = formatBackstopComment(backstopResult);
2247
+ if (backstopComment) {
2248
+ try {
2249
+ await this.client.createComment(issueId, backstopComment);
2250
+ }
2251
+ catch {
2252
+ // Best-effort diagnostic comment
2253
+ }
2254
+ }
2255
+ }
2256
+ // If backstop recovered the PR URL, update the session
2257
+ if (agent.pullRequestUrl && sessionId) {
2258
+ try {
2259
+ await this.updateSessionPullRequest(sessionId, agent.pullRequestUrl, agent);
2260
+ }
2261
+ catch {
2262
+ // Best-effort session update
2263
+ }
2264
+ }
2265
+ }
2216
2266
  // Update Linear status based on work type if auto-transition is enabled
2217
2267
  if (agent.status === 'completed' && this.config.autoTransition) {
2218
2268
  const workType = agent.workType ?? 'development';
@@ -2271,22 +2321,14 @@ ORCHESTRATOR_INSTALL=1 exec pnpm add "$@"
2271
2321
  const incompleteCheck = checkForIncompleteWork(agent.worktreePath);
2272
2322
  if (incompleteCheck.hasIncompleteWork) {
2273
2323
  // Agent has uncommitted/unpushed changes — block promotion
2324
+ // The diagnostic comment is posted in the cleanup section AFTER the
2325
+ // worktree preservation is confirmed — not here, to avoid promising
2326
+ // preservation before it actually happens.
2274
2327
  log?.error('Code-producing agent completed without PR and has incomplete work — blocking promotion', {
2275
2328
  workType,
2276
2329
  reason: incompleteCheck.reason,
2277
2330
  details: incompleteCheck.details,
2278
2331
  });
2279
- // Post a diagnostic comment
2280
- try {
2281
- await this.client.createComment(issueId, `⚠️ **Agent completed but work was not persisted.**\n\n` +
2282
- `The agent reported success but no PR was detected, and the worktree has ${incompleteCheck.details}.\n\n` +
2283
- `**Issue status was NOT promoted** to prevent lost work from advancing through the pipeline.\n\n` +
2284
- `The worktree has been preserved at \`${agent.worktreePath}\`. ` +
2285
- `To recover: cd into the worktree, commit, push, and create a PR manually.`);
2286
- }
2287
- catch {
2288
- // Best-effort comment
2289
- }
2290
2332
  // Do NOT set targetStatus — leave issue in current state
2291
2333
  }
2292
2334
  else {
@@ -2411,6 +2453,34 @@ ORCHESTRATOR_INSTALL=1 exec pnpm add "$@"
2411
2453
  catch {
2412
2454
  // Best-effort - heartbeat will go stale naturally after timeout
2413
2455
  }
2456
+ // Write a .preserved marker so branch conflict resolution knows not to
2457
+ // destroy this worktree. The marker includes context for diagnostics.
2458
+ try {
2459
+ const agentDir = resolve(agent.worktreePath, '.agent');
2460
+ if (!existsSync(agentDir)) {
2461
+ mkdirSync(agentDir, { recursive: true });
2462
+ }
2463
+ writeFileSync(resolve(agentDir, 'preserved.json'), JSON.stringify({
2464
+ preservedAt: new Date().toISOString(),
2465
+ issueId,
2466
+ reason: incompleteCheck.reason,
2467
+ details: incompleteCheck.details,
2468
+ }, null, 2));
2469
+ }
2470
+ catch {
2471
+ // Best-effort - the shouldCleanup=false flag is the primary guard
2472
+ }
2473
+ // Post diagnostic comment NOW that preservation is confirmed
2474
+ try {
2475
+ await this.client.createComment(issueId, `⚠️ **Agent completed but work was not persisted.**\n\n` +
2476
+ `The agent reported success but no PR was detected, and the worktree has ${incompleteCheck.details}.\n\n` +
2477
+ `**Issue status was NOT promoted** to prevent lost work from advancing through the pipeline.\n\n` +
2478
+ `The worktree has been preserved at \`${agent.worktreePath}\`. ` +
2479
+ `To recover: cd into the worktree, commit, push, and create a PR manually.`);
2480
+ }
2481
+ catch {
2482
+ // Best-effort comment
2483
+ }
2414
2484
  }
2415
2485
  else {
2416
2486
  // No PR but also no local changes - agent may not have made any changes
@@ -2496,6 +2566,23 @@ ORCHESTRATOR_INSTALL=1 exec pnpm add "$@"
2496
2566
  catch {
2497
2567
  // Best-effort - heartbeat will go stale naturally after timeout
2498
2568
  }
2569
+ // Write a .preserved marker so branch conflict resolution knows not to
2570
+ // destroy this worktree
2571
+ try {
2572
+ const agentDir = resolve(agent.worktreePath, '.agent');
2573
+ if (!existsSync(agentDir)) {
2574
+ mkdirSync(agentDir, { recursive: true });
2575
+ }
2576
+ writeFileSync(resolve(agentDir, 'preserved.json'), JSON.stringify({
2577
+ preservedAt: new Date().toISOString(),
2578
+ issueId,
2579
+ reason: incompleteCheck.reason,
2580
+ details: incompleteCheck.details,
2581
+ }, null, 2));
2582
+ }
2583
+ catch {
2584
+ // Best-effort - the shouldCleanup=false flag is the primary guard
2585
+ }
2499
2586
  }
2500
2587
  }
2501
2588
  if (shouldCleanup && agent.worktreeIdentifier) {
@@ -2622,6 +2709,8 @@ ORCHESTRATOR_INSTALL=1 exec pnpm add "$@"
2622
2709
  heartbeatWriter?.recordToolCall(event.toolName);
2623
2710
  progressLogger?.logTool(event.toolName, event.input);
2624
2711
  sessionLogger?.logToolUse(event.toolName, event.input);
2712
+ // Track session output signals for completion contract validation
2713
+ this.trackSessionOutputSignal(issueId, event.toolName, event.input);
2625
2714
  // Intercept TodoWrite tool calls to persist todos
2626
2715
  if (event.toolName === 'TodoWrite') {
2627
2716
  try {
@@ -2789,6 +2878,45 @@ ORCHESTRATOR_INSTALL=1 exec pnpm add "$@"
2789
2878
  /**
2790
2879
  * Extract GitHub PR URL from text (typically from gh pr create output)
2791
2880
  */
2881
+ /**
2882
+ * Track session output signals from tool calls for completion contract validation.
2883
+ * Detects when agents call Linear CLI or MCP tools that produce required outputs.
2884
+ */
2885
+ trackSessionOutputSignal(issueId, toolName, input) {
2886
+ let flags = this.sessionOutputFlags.get(issueId);
2887
+ if (!flags) {
2888
+ flags = { commentPosted: false, issueUpdated: false, subIssuesCreated: false };
2889
+ this.sessionOutputFlags.set(issueId, flags);
2890
+ }
2891
+ // Detect comment creation (CLI via Bash or MCP tool)
2892
+ if (toolName === 'af_linear_create_comment' ||
2893
+ toolName === 'mcp__af-linear__af_linear_create_comment') {
2894
+ flags.commentPosted = true;
2895
+ }
2896
+ // Detect issue update (CLI via Bash or MCP tool)
2897
+ if (toolName === 'af_linear_update_issue' ||
2898
+ toolName === 'mcp__af-linear__af_linear_update_issue') {
2899
+ flags.issueUpdated = true;
2900
+ }
2901
+ // Detect issue creation (CLI via Bash or MCP tool)
2902
+ if (toolName === 'af_linear_create_issue' ||
2903
+ toolName === 'mcp__af-linear__af_linear_create_issue') {
2904
+ flags.subIssuesCreated = true;
2905
+ }
2906
+ // Detect Bash tool calls that invoke the Linear CLI
2907
+ if (toolName === 'Bash') {
2908
+ const command = typeof input?.command === 'string' ? input.command : '';
2909
+ if (command.includes('af-linear create-comment') || command.includes('af-linear create_comment')) {
2910
+ flags.commentPosted = true;
2911
+ }
2912
+ if (command.includes('af-linear update-issue') || command.includes('af-linear update_issue')) {
2913
+ flags.issueUpdated = true;
2914
+ }
2915
+ if (command.includes('af-linear create-issue') || command.includes('af-linear create_issue')) {
2916
+ flags.subIssuesCreated = true;
2917
+ }
2918
+ }
2919
+ }
2792
2920
  extractPullRequestUrl(text) {
2793
2921
  // GitHub PR URL pattern: https://github.com/owner/repo/pull/123
2794
2922
  const prUrlPattern = /https:\/\/github\.com\/[^/]+\/[^/]+\/pull\/\d+/g;
@@ -2924,6 +3052,8 @@ ORCHESTRATOR_INSTALL=1 exec pnpm add "$@"
2924
3052
  progressLogger.stop();
2925
3053
  this.progressLoggers.delete(issueId);
2926
3054
  }
3055
+ // Cleanup session output flags
3056
+ this.sessionOutputFlags.delete(issueId);
2927
3057
  // Persist and cleanup context manager
2928
3058
  const contextManager = this.contextManagers.get(issueId);
2929
3059
  if (contextManager) {
@@ -3650,7 +3780,7 @@ ORCHESTRATOR_INSTALL=1 exec pnpm add "$@"
3650
3780
  workType: workType ?? 'development',
3651
3781
  });
3652
3782
  // Create in-process tool servers from registered plugins
3653
- const mcpServers = spawnProviderName === 'claude'
3783
+ const toolServers = spawnProviderName === 'claude'
3654
3784
  ? this.toolRegistry.createServers({ env, cwd: worktreePath ?? process.cwd() })
3655
3785
  : undefined;
3656
3786
  // Coordinators need significantly more turns than standard agents
@@ -3665,7 +3795,8 @@ ORCHESTRATOR_INSTALL=1 exec pnpm add "$@"
3665
3795
  abortController,
3666
3796
  autonomous: true,
3667
3797
  sandboxEnabled: this.config.sandboxEnabled,
3668
- mcpServers,
3798
+ mcpServers: toolServers?.servers,
3799
+ mcpToolNames: toolServers?.toolNames,
3669
3800
  maxTurns,
3670
3801
  onProcessSpawned: (pid) => {
3671
3802
  agent.pid = pid;
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Session Backstop
3
+ *
4
+ * Deterministic post-session recovery that runs after every agent session.
5
+ * Validates the session's outputs against the work type's completion contract,
6
+ * then takes backstop actions for any recoverable gaps.
7
+ *
8
+ * This is provider-agnostic — it operates on the worktree and GitHub API,
9
+ * not on the agent session. Every provider gets the same backstop.
10
+ *
11
+ * Architecture:
12
+ * 1. Collect session outputs (git state, PR detection, work result markers)
13
+ * 2. Validate against the completion contract
14
+ * 3. Run backstop actions for recoverable fields (push, create PR)
15
+ * 4. Return structured result for the orchestrator to act on
16
+ */
17
+ import type { AgentProcess } from './types.js';
18
+ import type { BackstopResult, CompletionContract, CompletionValidationResult, SessionOutputs } from './completion-contracts.js';
19
+ /** Context needed to collect session outputs */
20
+ export interface SessionContext {
21
+ agent: AgentProcess;
22
+ /** Whether the agent posted at least one comment to the issue */
23
+ commentPosted: boolean;
24
+ /** Whether the agent updated the issue description */
25
+ issueUpdated: boolean;
26
+ /** Whether the agent created sub-issues */
27
+ subIssuesCreated: boolean;
28
+ }
29
+ /**
30
+ * Collect structured outputs from the completed session.
31
+ * Inspects git state, agent process data, and tracked flags.
32
+ */
33
+ export declare function collectSessionOutputs(ctx: SessionContext): SessionOutputs;
34
+ /** Options for running the backstop */
35
+ export interface BackstopOptions {
36
+ /** Skip destructive backstop actions (push, PR creation) — useful for dry-run */
37
+ dryRun?: boolean;
38
+ /** PR title template. {identifier} is replaced with the issue key */
39
+ prTitleTemplate?: string;
40
+ /** PR body template. {identifier} is replaced with the issue key */
41
+ prBodyTemplate?: string;
42
+ }
43
+ /**
44
+ * Run the post-session backstop for an agent.
45
+ *
46
+ * 1. Collects session outputs
47
+ * 2. Validates against completion contract
48
+ * 3. Runs backstop actions for recoverable gaps
49
+ * 4. Returns structured result
50
+ *
51
+ * This function is safe to call for any work type — it returns a no-op
52
+ * result for work types without contracts.
53
+ */
54
+ export declare function runBackstop(ctx: SessionContext, options?: BackstopOptions): BackstopRunResult;
55
+ /** Full result of a backstop run */
56
+ export interface BackstopRunResult {
57
+ contract: CompletionContract | null;
58
+ outputs: SessionOutputs;
59
+ validation: CompletionValidationResult | null;
60
+ backstop: BackstopResult;
61
+ diagnosticMessage: string | null;
62
+ }
63
+ /**
64
+ * Format a backstop result into a diagnostic comment for the issue tracker.
65
+ */
66
+ export declare function formatBackstopComment(result: BackstopRunResult): string | null;
67
+ //# sourceMappingURL=session-backstop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-backstop.d.ts","sourceRoot":"","sources":["../../../src/orchestrator/session-backstop.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE9C,OAAO,KAAK,EAEV,cAAc,EACd,kBAAkB,EAClB,0BAA0B,EAC1B,cAAc,EACf,MAAM,2BAA2B,CAAA;AAWlC,gDAAgD;AAChD,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,YAAY,CAAA;IACnB,iEAAiE;IACjE,aAAa,EAAE,OAAO,CAAA;IACtB,sDAAsD;IACtD,YAAY,EAAE,OAAO,CAAA;IACrB,2CAA2C;IAC3C,gBAAgB,EAAE,OAAO,CAAA;CAC1B;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,cAAc,GAAG,cAAc,CA8BzE;AAMD,uCAAuC;AACvC,MAAM,WAAW,eAAe;IAC9B,iFAAiF;IACjF,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,qEAAqE;IACrE,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,oEAAoE;IACpE,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CACzB,GAAG,EAAE,cAAc,EACnB,OAAO,CAAC,EAAE,eAAe,GACxB,iBAAiB,CAkGnB;AAED,oCAAoC;AACpC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,kBAAkB,GAAG,IAAI,CAAA;IACnC,OAAO,EAAE,cAAc,CAAA;IACvB,UAAU,EAAE,0BAA0B,GAAG,IAAI,CAAA;IAC7C,QAAQ,EAAE,cAAc,CAAA;IACxB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;CACjC;AAmOD;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,GAAG,IAAI,CA8C9E"}