@gajae-code/coding-agent 0.12.4 → 0.12.6

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 (139) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/README.md +4 -1
  3. package/dist/types/async/job-manager.d.ts +9 -1
  4. package/dist/types/eval/backend.d.ts +2 -2
  5. package/dist/types/exec/bash-executor.d.ts +10 -8
  6. package/dist/types/extensibility/extensions/runner.d.ts +9 -7
  7. package/dist/types/gjc-runtime/launch-tmux.d.ts +22 -3
  8. package/dist/types/gjc-runtime/psmux-detect.d.ts +3 -0
  9. package/dist/types/gjc-runtime/team-launch.d.ts +3 -1
  10. package/dist/types/gjc-runtime/team-runtime.d.ts +14 -2
  11. package/dist/types/gjc-runtime/team-store.d.ts +0 -4
  12. package/dist/types/gjc-runtime/team-workers.d.ts +5 -0
  13. package/dist/types/gjc-runtime/tmux-common.d.ts +1 -0
  14. package/dist/types/gjc-runtime/tmux-provider-context.d.ts +57 -0
  15. package/dist/types/gjc-runtime/tmux-sessions.d.ts +8 -1
  16. package/dist/types/gjc-runtime/ultragoal-change-set.d.ts +3 -0
  17. package/dist/types/gjc-runtime/ultragoal-review-source.d.ts +83 -0
  18. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +57 -4
  19. package/dist/types/internal-urls/registry-helpers.d.ts +18 -0
  20. package/dist/types/modes/components/gjc-bundle-settings.d.ts +1 -0
  21. package/dist/types/modes/components/plugin-settings.d.ts +2 -0
  22. package/dist/types/modes/components/settings-selector.d.ts +2 -0
  23. package/dist/types/sdk/broker/broker.d.ts +18 -0
  24. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +13 -0
  25. package/dist/types/sdk/broker/lifecycle.d.ts +1 -0
  26. package/dist/types/sdk/bus/chat-daemon-control.d.ts +5 -2
  27. package/dist/types/sdk/bus/index.d.ts +4 -0
  28. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +7 -3
  29. package/dist/types/sdk/bus/telegram-daemon.d.ts +25 -3
  30. package/dist/types/sdk/bus/topic-registry.d.ts +63 -3
  31. package/dist/types/sdk/host/host.d.ts +23 -2
  32. package/dist/types/sdk/host/reverse-leases.d.ts +7 -4
  33. package/dist/types/session/agent-session.d.ts +5 -4
  34. package/dist/types/session/artifacts.d.ts +8 -5
  35. package/dist/types/session/attempt-record-store.d.ts +19 -0
  36. package/dist/types/session/client-bridge.d.ts +14 -0
  37. package/dist/types/session/internal/managed-session-storage.d.ts +7 -23
  38. package/dist/types/session/messages.d.ts +4 -1
  39. package/dist/types/session/session-manager.d.ts +7 -5
  40. package/dist/types/session/session-storage.d.ts +8 -0
  41. package/dist/types/session/streaming-output.d.ts +32 -0
  42. package/dist/types/task/executor.d.ts +1 -1
  43. package/dist/types/task/index.d.ts +2 -1
  44. package/dist/types/task/receipt.d.ts +3 -0
  45. package/dist/types/task/types.d.ts +194 -0
  46. package/dist/types/task/worktree.d.ts +13 -6
  47. package/dist/types/tools/bash.d.ts +1 -1
  48. package/dist/types/tools/fetch.d.ts +3 -0
  49. package/dist/types/tools/output-meta.d.ts +37 -5
  50. package/dist/types/tools/subagent.d.ts +2 -0
  51. package/dist/types/tools/tool-result.d.ts +1 -0
  52. package/dist/types/utils/artifact-id.d.ts +2 -0
  53. package/dist/types/utils/git.d.ts +1 -0
  54. package/package.json +8 -8
  55. package/scripts/capture-sticky-viewport-showcase.ts +131 -5
  56. package/scripts/verify-gjc-sdk-canonicalization.ts +128 -183
  57. package/scripts/verify-sticky-viewport-showcase.ts +620 -93
  58. package/src/async/job-manager.ts +13 -5
  59. package/src/config/model-profiles.ts +8 -0
  60. package/src/config/settings-schema.ts +17 -3
  61. package/src/cursor.ts +20 -13
  62. package/src/defaults/gjc/skills/ultragoal/SKILL.md +5 -2
  63. package/src/eval/backend.ts +2 -2
  64. package/src/exec/bash-executor.ts +425 -151
  65. package/src/extensibility/extensions/runner.ts +80 -6
  66. package/src/extensibility/extensions/wrapper.ts +22 -15
  67. package/src/extensibility/runtime-skill-discovery.ts +4 -6
  68. package/src/gjc-runtime/launch-tmux.ts +309 -293
  69. package/src/gjc-runtime/managed-owner-supervisor.ts +6 -2
  70. package/src/gjc-runtime/psmux-detect.ts +52 -6
  71. package/src/gjc-runtime/team-launch.ts +50 -2
  72. package/src/gjc-runtime/team-runtime.ts +716 -251
  73. package/src/gjc-runtime/team-store.ts +25 -7
  74. package/src/gjc-runtime/team-workers.ts +161 -1
  75. package/src/gjc-runtime/tmux-common.ts +16 -4
  76. package/src/gjc-runtime/tmux-gc.ts +37 -5
  77. package/src/gjc-runtime/tmux-owner-isolation.ts +2 -0
  78. package/src/gjc-runtime/tmux-provider-context.ts +451 -0
  79. package/src/gjc-runtime/tmux-sessions.ts +409 -156
  80. package/src/gjc-runtime/ultragoal-change-set.ts +90 -44
  81. package/src/gjc-runtime/ultragoal-receipt-freshness.ts +7 -0
  82. package/src/gjc-runtime/ultragoal-review-source.ts +378 -0
  83. package/src/gjc-runtime/ultragoal-runtime.ts +640 -159
  84. package/src/gjc-runtime/workflow-manifest.generated.json +40 -1
  85. package/src/gjc-runtime/workflow-manifest.ts +12 -0
  86. package/src/internal-urls/agent-protocol.ts +98 -38
  87. package/src/internal-urls/artifact-protocol.ts +28 -17
  88. package/src/internal-urls/docs-index.generated.ts +6 -6
  89. package/src/internal-urls/local-protocol.ts +39 -18
  90. package/src/internal-urls/registry-helpers.ts +62 -0
  91. package/src/modes/acp/acp-agent.ts +51 -13
  92. package/src/modes/acp/acp-client-bridge.ts +154 -11
  93. package/src/modes/components/gjc-bundle-settings.ts +2 -0
  94. package/src/modes/components/plugin-settings.ts +5 -0
  95. package/src/modes/components/settings-selector.ts +4 -0
  96. package/src/modes/controllers/selector-controller.ts +1 -0
  97. package/src/prompts/tools/bash.md +2 -2
  98. package/src/prompts/tools/monitor.md +4 -4
  99. package/src/sdk/acp/adapter.ts +200 -9
  100. package/src/sdk/broker/broker.ts +93 -10
  101. package/src/sdk/broker/lifecycle-ledger.ts +227 -11
  102. package/src/sdk/broker/lifecycle.ts +692 -98
  103. package/src/sdk/bus/chat-daemon-control.ts +33 -5
  104. package/src/sdk/bus/index.ts +347 -14
  105. package/src/sdk/bus/telegram-daemon-contract.ts +7 -3
  106. package/src/sdk/bus/telegram-daemon.ts +283 -44
  107. package/src/sdk/bus/topic-registry.ts +133 -10
  108. package/src/sdk/host/host.ts +187 -9
  109. package/src/sdk/host/reverse-leases.ts +249 -42
  110. package/src/sdk/session.ts +8 -6
  111. package/src/session/agent-session.ts +930 -354
  112. package/src/session/artifacts.ts +211 -74
  113. package/src/session/attempt-record-store.ts +106 -0
  114. package/src/session/client-bridge.ts +15 -0
  115. package/src/session/internal/managed-session-scope.ts +332 -198
  116. package/src/session/internal/managed-session-storage.ts +162 -450
  117. package/src/session/messages.ts +32 -7
  118. package/src/session/session-manager.ts +31 -28
  119. package/src/session/session-storage.ts +190 -53
  120. package/src/session/streaming-output.ts +198 -40
  121. package/src/task/executor.ts +5 -2
  122. package/src/task/index.ts +277 -64
  123. package/src/task/provider-retry-status.ts +4 -1
  124. package/src/task/receipt.ts +26 -2
  125. package/src/task/render.ts +42 -1
  126. package/src/task/types.ts +52 -0
  127. package/src/task/worktree.ts +122 -43
  128. package/src/tools/bash.ts +818 -239
  129. package/src/tools/eval.ts +99 -4
  130. package/src/tools/fetch.ts +77 -15
  131. package/src/tools/find.ts +7 -3
  132. package/src/tools/monitor.ts +7 -1
  133. package/src/tools/output-meta.ts +706 -116
  134. package/src/tools/search.ts +9 -4
  135. package/src/tools/subagent-render.ts +5 -2
  136. package/src/tools/subagent.ts +8 -0
  137. package/src/tools/tool-result.ts +1 -0
  138. package/src/utils/artifact-id.ts +6 -0
  139. package/src/utils/git.ts +2 -0
@@ -194,14 +194,39 @@ function utf8Suffix(text: string, maxBytes: number): string {
194
194
  * next provider context is constructed. Byte boundaries never split UTF-8
195
195
  * code points, so head/tail recovery remains readable for emoji and CJK text.
196
196
  */
197
- export function createPreAdmissionArtifactSpillPreview(fullText: string, artifactId: string, digest: string): string {
197
+ export function createPreAdmissionArtifactSpillPreview(
198
+ fullText: string,
199
+ artifactId: string,
200
+ digest: string,
201
+ options: { maxInlineBytes?: number; fullOutput?: boolean } = {},
202
+ ): string {
198
203
  const totalBytes = Buffer.byteLength(fullText, "utf-8");
199
- const head = utf8Prefix(fullText, PRE_ADMISSION_ARTIFACT_SPILL_HEAD_BYTES);
200
- const tail = utf8Suffix(fullText, PRE_ADMISSION_ARTIFACT_SPILL_TAIL_BYTES);
201
- const retainedBytes = Buffer.byteLength(head, "utf-8") + Buffer.byteLength(tail, "utf-8");
202
- const omittedBytes = Math.max(0, totalBytes - retainedBytes);
203
- const receipt = `[${omittedBytes} bytes omitted; sha256:${digest}; full output: artifact://${artifactId}]`;
204
- return `${head}\n\n${receipt}\n\n${tail}`;
204
+ const maxInlineBytes = options.maxInlineBytes;
205
+ const referenceKind = options.fullOutput === false ? "retained output" : "full output";
206
+ let headBudget = PRE_ADMISSION_ARTIFACT_SPILL_HEAD_BYTES;
207
+ let tailBudget = PRE_ADMISSION_ARTIFACT_SPILL_TAIL_BYTES;
208
+ if (maxInlineBytes !== undefined) {
209
+ const receiptReserve = Buffer.byteLength(
210
+ `\n\n[${totalBytes} bytes omitted; sha256:${digest}; ${referenceKind}: artifact://${artifactId}]\n\n`,
211
+ "utf-8",
212
+ );
213
+ const contentBudget = Math.max(0, maxInlineBytes - receiptReserve);
214
+ headBudget = Math.min(headBudget, Math.ceil(contentBudget / 2));
215
+ tailBudget = Math.min(tailBudget, Math.floor(contentBudget / 2));
216
+ }
217
+ for (;;) {
218
+ const head = utf8Prefix(fullText, headBudget);
219
+ const tail = utf8Suffix(fullText, tailBudget);
220
+ const retainedBytes = Buffer.byteLength(head, "utf-8") + Buffer.byteLength(tail, "utf-8");
221
+ const omittedBytes = Math.max(0, totalBytes - retainedBytes);
222
+ const receipt = `[${omittedBytes} bytes omitted; sha256:${digest}; ${referenceKind}: artifact://${artifactId}]`;
223
+ const preview = `${head}\n\n${receipt}\n\n${tail}`;
224
+ if (maxInlineBytes === undefined || Buffer.byteLength(preview, "utf-8") <= maxInlineBytes) return preview;
225
+ if (headBudget === 0 && tailBudget === 0) return utf8Prefix(receipt, maxInlineBytes);
226
+ const overflow = Buffer.byteLength(preview, "utf-8") - maxInlineBytes;
227
+ if (tailBudget >= headBudget) tailBudget = Math.max(0, tailBudget - overflow);
228
+ else headBudget = Math.max(0, headBudget - overflow);
229
+ }
205
230
  }
206
231
 
207
232
  /**
@@ -37,7 +37,7 @@ import { assertSafePathComponent } from "../gjc-runtime/session-layout";
37
37
  import { writeTextAtomic } from "../gjc-runtime/state-writer";
38
38
  import type { ManagedLegacyLocalMigrationSource } from "../internal-urls/local-protocol";
39
39
  import * as git from "../utils/git";
40
- import { ArtifactManager } from "./artifacts";
40
+ import { ArtifactManager, type ArtifactSaveReceipt } from "./artifacts";
41
41
  import {
42
42
  type BlobPutResult,
43
43
  BlobStore,
@@ -895,6 +895,7 @@ export interface ResumeSessionIdentity {
895
895
  sessionId: string;
896
896
  dev: bigint;
897
897
  ino: bigint;
898
+ nlink?: bigint;
898
899
  size: number;
899
900
  mtimeMs: number;
900
901
  mtimeNs: bigint;
@@ -1132,7 +1133,7 @@ export function createReadonlySessionManager(manager: SessionManager): ReadonlyS
1132
1133
 
1133
1134
  /** Internal artifact-writing capability. Read-only facades expose it only through a private weak-map lookup. */
1134
1135
  export type SessionArtifactCapability = Readonly<
1135
- Pick<SessionManager, "allocateArtifactPath" | "saveArtifact" | "putBlob">
1136
+ Pick<SessionManager, "allocateArtifactPath" | "saveArtifact" | "getArtifactPath" | "putBlob">
1136
1137
  >;
1137
1138
 
1138
1139
  const sessionArtifactCapabilities = new WeakMap<SessionManager, SessionArtifactCapability>();
@@ -1149,6 +1150,7 @@ export function sessionArtifactCapability(value: unknown): SessionArtifactCapabi
1149
1150
  capability = Object.freeze({
1150
1151
  allocateArtifactPath: value.allocateArtifactPath.bind(value),
1151
1152
  saveArtifact: value.saveArtifact.bind(value),
1153
+ getArtifactPath: value.getArtifactPath.bind(value),
1152
1154
  putBlob: value.putBlob.bind(value),
1153
1155
  });
1154
1156
  sessionArtifactCapabilities.set(value, capability);
@@ -1831,6 +1833,7 @@ function retainedTreeSnapshotEquals(
1831
1833
  entry.kind === other.kind &&
1832
1834
  entry.dev === other.dev &&
1833
1835
  entry.ino === other.ino &&
1836
+ entry.nlink === other.nlink &&
1834
1837
  entry.size === other.size &&
1835
1838
  entry.mtimeNs === other.mtimeNs &&
1836
1839
  entry.ctimeNs === other.ctimeNs &&
@@ -1856,6 +1859,7 @@ function retainedTreeSnapshotEqualsAfterRename(
1856
1859
  entry.kind === other.kind &&
1857
1860
  entry.dev === other.dev &&
1858
1861
  entry.ino === other.ino &&
1862
+ entry.nlink === other.nlink &&
1859
1863
  entry.size === other.size &&
1860
1864
  entry.mtimeNs === other.mtimeNs &&
1861
1865
  (entry.relativePath === "" || entry.ctimeNs === other.ctimeNs) &&
@@ -2147,6 +2151,7 @@ function sameResumeIdentity(left: ResumeSessionIdentity, right: ResumeSessionIde
2147
2151
  left.sessionId === right.sessionId &&
2148
2152
  left.dev === right.dev &&
2149
2153
  left.ino === right.ino &&
2154
+ left.nlink === right.nlink &&
2150
2155
  left.size === right.size &&
2151
2156
  left.mtimeMs === right.mtimeMs &&
2152
2157
  left.mtimeNs === right.mtimeNs &&
@@ -2160,6 +2165,7 @@ function sameResumeStat(left: SessionStorageStat, right: SessionStorageStat): bo
2160
2165
  right.isFile &&
2161
2166
  left.dev === right.dev &&
2162
2167
  left.ino === right.ino &&
2168
+ left.nlink === right.nlink &&
2163
2169
  left.size === right.size &&
2164
2170
  left.mtimeMs === right.mtimeMs &&
2165
2171
  left.mtimeNs === right.mtimeNs
@@ -2356,6 +2362,7 @@ function inspectResumeSessionFile(
2356
2362
  sessionId: header.id,
2357
2363
  dev: snapshot.dev,
2358
2364
  ino: snapshot.ino,
2365
+ nlink: snapshot.nlink,
2359
2366
  size: snapshot.size,
2360
2367
  mtimeMs: snapshot.mtimeMs,
2361
2368
  mtimeNs: snapshot.mtimeNs,
@@ -2393,6 +2400,7 @@ function revalidateResumeSessionIdentity(
2393
2400
  sessionId: expected.sessionId,
2394
2401
  dev: snapshot.stat.dev,
2395
2402
  ino: snapshot.stat.ino,
2403
+ nlink: snapshot.stat.nlink,
2396
2404
  size: snapshot.stat.size,
2397
2405
  mtimeMs: snapshot.stat.mtimeMs,
2398
2406
  mtimeNs: snapshot.stat.mtimeNs,
@@ -4764,10 +4772,6 @@ export class SessionManager {
4764
4772
  // Subagents adopt the parent's manager so artifact IDs are unique across the
4765
4773
  // whole agent tree and all files land in the parent's artifacts dir.
4766
4774
  #adoptedArtifactManager: ArtifactManager | null = null;
4767
- // In-memory artifact fallback for non-persistent sessions (persist=false).
4768
- // Keyed by sequential numeric ID string; mirrors the file-based ArtifactManager ID scheme.
4769
- #inMemoryArtifacts: Map<string, string> | null = null;
4770
- #inMemoryArtifactCounter = 0;
4771
4775
  readonly #blobStore: BlobStore;
4772
4776
  #residentTextBlobStore: BlobStore = new MemoryBlobStore();
4773
4777
  #residentImageBlobStore: BlobStore;
@@ -5361,8 +5365,6 @@ export class SessionManager {
5361
5365
  this.#flushed = false;
5362
5366
  this.#needsFullRewriteOnNextPersist = false;
5363
5367
  this.#ensuredOnDisk = false;
5364
- this.#inMemoryArtifacts = null;
5365
- this.#inMemoryArtifactCounter = 0;
5366
5368
  this.#artifactManager = null;
5367
5369
  this.#artifactManagerSessionFile = null;
5368
5370
  this.#adoptedArtifactManager = null;
@@ -5581,8 +5583,6 @@ export class SessionManager {
5581
5583
  flushed: this.#flushed,
5582
5584
  needsFullRewriteOnNextPersist: this.#needsFullRewriteOnNextPersist,
5583
5585
  ensuredOnDisk: this.#ensuredOnDisk,
5584
- inMemoryArtifacts: this.#inMemoryArtifacts,
5585
- inMemoryArtifactCounter: this.#inMemoryArtifactCounter,
5586
5586
  artifactManager: this.#artifactManager,
5587
5587
  artifactManagerSessionFile: this.#artifactManagerSessionFile,
5588
5588
  adoptedArtifactManager: this.#adoptedArtifactManager,
@@ -5605,8 +5605,6 @@ export class SessionManager {
5605
5605
  this.#flushed = previous.flushed;
5606
5606
  this.#needsFullRewriteOnNextPersist = previous.needsFullRewriteOnNextPersist;
5607
5607
  this.#ensuredOnDisk = previous.ensuredOnDisk;
5608
- this.#inMemoryArtifacts = previous.inMemoryArtifacts;
5609
- this.#inMemoryArtifactCounter = previous.inMemoryArtifactCounter;
5610
5608
  this.#artifactManager = previous.artifactManager;
5611
5609
  this.#artifactManagerSessionFile = previous.artifactManagerSessionFile;
5612
5610
  this.#adoptedArtifactManager = previous.adoptedArtifactManager;
@@ -6052,8 +6050,6 @@ export class SessionManager {
6052
6050
  this.#flushed = stage.flushed;
6053
6051
  this.#needsFullRewriteOnNextPersist = false;
6054
6052
  this.#ensuredOnDisk = stage.flushed;
6055
- this.#inMemoryArtifacts = null;
6056
- this.#inMemoryArtifactCounter = 0;
6057
6053
  this.#artifactManager = null;
6058
6054
  this.#artifactManagerSessionFile = null;
6059
6055
  this.#adoptedArtifactManager = null;
@@ -7983,17 +7979,19 @@ export class SessionManager {
7983
7979
  }
7984
7980
 
7985
7981
  /**
7986
- * Save artifact content under the current session and return artifact ID.
7987
- * Returns an artifact ID for all sessions (file-backed for persistent, in-memory fallback otherwise).
7982
+ * Save artifact content under the current persisted session and return its artifact ID.
7983
+ * In-memory sessions cannot produce protocol-resolvable artifact references.
7988
7984
  */
7989
7985
  async saveArtifact(content: string, toolType: string): Promise<string | undefined> {
7990
7986
  const manager = this.#getOrCreateArtifactManager();
7991
7987
  if (manager) return manager.save(content, toolType);
7992
- // Non-persistent session: store in memory so spill truncation can proceed.
7993
- if (!this.#inMemoryArtifacts) this.#inMemoryArtifacts = new Map();
7994
- const id = String(this.#inMemoryArtifactCounter++);
7995
- this.#inMemoryArtifacts.set(id, content);
7996
- return id;
7988
+ return undefined;
7989
+ }
7990
+
7991
+ async saveArtifactReceipt(content: string, toolType: string): Promise<ArtifactSaveReceipt | undefined> {
7992
+ const manager = this.#getOrCreateArtifactManager();
7993
+ if (!manager) return undefined;
7994
+ return manager.saveWithReceipt(content, toolType);
7997
7995
  }
7998
7996
 
7999
7997
  /**
@@ -9708,6 +9706,7 @@ export class SessionManager {
9708
9706
  destination: SessionDestination,
9709
9707
  store: ManagedSessionDescendantStore,
9710
9708
  storage: SessionStorage = new FileSessionStorage(),
9709
+ cwdOverride?: string,
9711
9710
  ): Promise<SessionManager> {
9712
9711
  if (destination.kind !== "managed" || !trustedSessionDestinations.has(destination))
9713
9712
  throw new Error("Nested managed session authority is unavailable");
@@ -9721,16 +9720,20 @@ export class SessionManager {
9721
9720
  const captured = store.readExpected(path.basename(resolved));
9722
9721
  const entries = captured ? parseSessionEntries(captured.bytes.toString("utf8")) : [];
9723
9722
  const header = entries.find(entry => entry.type === "session") as SessionHeader | undefined;
9724
- const manager = new SessionManager(
9725
- header?.cwd ?? getProjectDir(),
9726
- destination.directory,
9727
- true,
9728
- storage,
9729
- destination,
9723
+ const sessionCwd = cwdOverride ? path.resolve(cwdOverride) : (header?.cwd ?? getProjectDir());
9724
+ const cwdChanged = Boolean(
9725
+ header && cwdOverride && resolveEquivalentPath(header.cwd) !== resolveEquivalentPath(sessionCwd),
9730
9726
  );
9727
+ if (header && cwdChanged) header.cwd = sessionCwd;
9728
+ const manager = new SessionManager(sessionCwd, destination.directory, true, storage, destination);
9731
9729
  if (entries.length > 0) {
9732
- const migrationApplied = migrateToCurrentVersion(entries);
9730
+ const migrationApplied = migrateToCurrentVersion(entries) || cwdChanged;
9733
9731
  await manager.#hydrateExistingSession(resolved, entries, migrationApplied, "memory-fallback");
9732
+ if (cwdChanged) {
9733
+ await manager.#rewriteFile();
9734
+ manager.#flushed = true;
9735
+ manager.#ensuredOnDisk = true;
9736
+ }
9734
9737
  writeTerminalBreadcrumb(manager.cwd, resolved);
9735
9738
  await manager.#sanitizeLoadedOpenAIResponsesReplayMetadataAndPersist();
9736
9739
  } else {