@agent-team-foundation/first-tree-hub 0.12.8 → 0.12.9

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.
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import "../observability-BAScT_5S-BcW9HgkG.mjs";
3
- import { $ as formatStaleReason, A as checkDocker, B as isServiceSupported, C as createApiNameResolver, D as checkBackgroundService, E as checkAgentConfigs, F as checkWebSocket, H as restartClientService, I as printResults, J as stopPostgres, L as reconcileAgentConfigs, M as checkServerConfig, N as checkServerHealth, O as checkClientConfig, P as checkServerReachable, Q as findStaleAliases, R as getClientServiceStatus, S as runHomeMigration, T as runMigrations, U as startClientService, W as stopClientService, X as handleClientOrgMismatch, Y as ClientRuntime, _ as formatCheckReport, a as declineUpdate, at as fail, b as onboardCreate, c as detectInstallMode, ct as ClientUserMismatchError, d as startServer, dt as SessionRegistry, et as removeLocalAgent, f as reconcileLocalRuntimeProviders, ft as cleanWorkspaces, g as promptMissingFields, h as promptAddAgent, ht as configureClientLoggerForService, i as createExecuteUpdate, it as resolveSenderName, j as checkNodeVersion, k as checkDatabase, l as fetchLatestVersion, lt as FirstTreeHubSDK, m as isInteractive, mt as applyClientLoggerConfig, o as promptUpdate, ot as success, p as uploadClientCapabilities, pt as probeCapabilities, r as registerSaaSConnectCommand, rt as resolveReplyToFromEnv, s as PACKAGE_NAME, st as ClientOrgMismatchError, tt as createOwner, u as installGlobalLatest, ut as SdkError, v as loadOnboardState, w as migrateLocalAgentDirs, x as saveOnboardState, y as onboardCheck, z as installClientService } from "../saas-connect-CLcon-De.mjs";
3
+ import { $ as formatStaleReason, A as checkDocker, B as isServiceSupported, C as createApiNameResolver, D as checkBackgroundService, E as checkAgentConfigs, F as checkWebSocket, H as restartClientService, I as printResults, J as stopPostgres, L as reconcileAgentConfigs, M as checkServerConfig, N as checkServerHealth, O as checkClientConfig, P as checkServerReachable, Q as findStaleAliases, R as getClientServiceStatus, S as runHomeMigration, T as runMigrations, U as startClientService, W as stopClientService, X as handleClientOrgMismatch, Y as ClientRuntime, _ as formatCheckReport, a as declineUpdate, at as fail, b as onboardCreate, c as detectInstallMode, ct as ClientUserMismatchError, d as startServer, dt as SessionRegistry, et as removeLocalAgent, f as reconcileLocalRuntimeProviders, ft as cleanWorkspaces, g as promptMissingFields, h as promptAddAgent, ht as configureClientLoggerForService, i as createExecuteUpdate, it as resolveSenderName, j as checkNodeVersion, k as checkDatabase, l as fetchLatestVersion, lt as FirstTreeHubSDK, m as isInteractive, mt as applyClientLoggerConfig, o as promptUpdate, ot as success, p as uploadClientCapabilities, pt as probeCapabilities, r as registerSaaSConnectCommand, rt as resolveReplyToFromEnv, s as PACKAGE_NAME, st as ClientOrgMismatchError, tt as createOwner, u as installGlobalLatest, ut as SdkError, v as loadOnboardState, w as migrateLocalAgentDirs, x as saveOnboardState, y as onboardCheck, z as installClientService } from "../saas-connect-CXZhK485.mjs";
4
4
  import "../logger-core-BTmvdflj-DjW8FM4T.mjs";
5
5
  import { C as resetConfigMeta, E as setConfigValue, S as resetConfig, T as serverConfigSchema, _ as getConfigValue, a as ensureFreshAdminToken, c as resolveServerUrl, d as DEFAULT_CONFIG_DIR, f as DEFAULT_DATA_DIR, h as clientConfigSchema, i as ensureFreshAccessToken, l as saveAgentConfig, m as agentConfigSchema, o as loadCredentials, p as DEFAULT_HOME_DIR, u as saveCredentials, v as initConfig, w as resolveConfigReadonly, x as readConfigFile, y as loadAgents } from "../bootstrap-BCZC1ki6.mjs";
6
6
  import { a as print, n as CLI_USER_AGENT, o as setJsonMode, r as COMMAND_VERSION, t as cliFetch } from "../cli-fetch--tiwKm5S.mjs";
@@ -8,7 +8,7 @@ import "../dist-CnjqakXS.mjs";
8
8
  import { n as bindFeishuUser, t as bindFeishuBot } from "../feishu-DrnBbl8T.mjs";
9
9
  import "../errors-CF5evtJt-B0NTIVPt.mjs";
10
10
  import "../src-DNBS5Yjj.mjs";
11
- import "../client-bR8nwHaV-OxnjyKOk.mjs";
11
+ import "../client-OMwJMCQt-R1T06ZH6.mjs";
12
12
  import "../invitation-Bg0TRiyx-BsZH4GCS.mjs";
13
13
  import { join } from "node:path";
14
14
  import { existsSync, mkdirSync, readFileSync, readdirSync } from "node:fs";
@@ -3,5 +3,5 @@ import "./logger-core-BTmvdflj-DjW8FM4T.mjs";
3
3
  import "./dist-CnjqakXS.mjs";
4
4
  import "./errors-CF5evtJt-B0NTIVPt.mjs";
5
5
  import "./src-DNBS5Yjj.mjs";
6
- import { J as listMyPinnedAgents } from "./client-bR8nwHaV-OxnjyKOk.mjs";
6
+ import { J as listMyPinnedAgents } from "./client-OMwJMCQt-R1T06ZH6.mjs";
7
7
  export { listMyPinnedAgents };
@@ -4,7 +4,7 @@ import { a as ConflictError, i as ClientUserMismatchError, l as organizations, n
4
4
  import { randomUUID } from "node:crypto";
5
5
  import { and, desc, eq, inArray, isNotNull, lt, ne, or, sql } from "drizzle-orm";
6
6
  import { bigserial, boolean, index, integer, jsonb, pgTable, primaryKey, text, timestamp, unique } from "drizzle-orm/pg-core";
7
- //#region ../server/dist/client-bR8nwHaV.mjs
7
+ //#region ../server/dist/client-OMwJMCQt.mjs
8
8
  /**
9
9
  * Client connections. A client is a single SDK process (AgentRuntime) that may
10
10
  * host multiple agents. From the unified-user-token milestone on, a client is
@@ -1720,6 +1720,12 @@ async function sendMessageInner(db, chatId, senderId, data, options) {
1720
1720
  }
1721
1721
  }
1722
1722
  if (data.format === "question") await assertSenderMayEmitQuestion(tx, senderId);
1723
+ const isSilentSend = typeof outboundContent === "string" && outboundContent.replace(/^(@\S+\s*)+/, "").trim().length === 0;
1724
+ if (isSilentSend) log.info({
1725
+ chatId,
1726
+ senderId,
1727
+ source: data.source ?? null
1728
+ }, "silent send: empty content after mention strip — no fan-out wake-up");
1723
1729
  const messageId = randomUUID();
1724
1730
  const [msg] = await tx.insert(messages).values({
1725
1731
  id: messageId,
@@ -1743,7 +1749,7 @@ async function sendMessageInner(db, chatId, senderId, data, options) {
1743
1749
  const fanout = participants.filter((p) => p.agentId !== senderId).map((p) => ({
1744
1750
  agentId: p.agentId,
1745
1751
  inboxId: p.inboxId,
1746
- notify: p.mode !== "mention_only" || mentionSet.has(p.agentId)
1752
+ notify: !isSilentSend && (p.mode !== "mention_only" || mentionSet.has(p.agentId))
1747
1753
  }));
1748
1754
  if (fanout.length > 0) await tx.insert(inboxEntries).values(fanout.map((f) => ({
1749
1755
  inboxId: f.inboxId,
@@ -1763,9 +1769,10 @@ async function sendMessageInner(db, chatId, senderId, data, options) {
1763
1769
  await tx.insert(inboxEntries).values({
1764
1770
  inboxId: original.replyToInbox,
1765
1771
  messageId,
1766
- chatId: original.replyToChat
1772
+ chatId: original.replyToChat,
1773
+ notify: !isSilentSend
1767
1774
  }).onConflictDoNothing();
1768
- if (!recipients.includes(original.replyToInbox)) recipients.push(original.replyToInbox);
1775
+ if (!isSilentSend && !recipients.includes(original.replyToInbox)) recipients.push(original.replyToInbox);
1769
1776
  }
1770
1777
  }
1771
1778
  await tx.update(chats).set({ updatedAt: /* @__PURE__ */ new Date() }).where(eq(chats.id, chatId));
@@ -1796,11 +1803,81 @@ async function sendMessageInner(db, chatId, senderId, data, options) {
1796
1803
  }, "predictive session activation failed");
1797
1804
  }
1798
1805
  fireChatMessageKick(chatId, txResult.message.id);
1806
+ observeLoopPattern(db, chatId).catch((err) => {
1807
+ log.error({
1808
+ err,
1809
+ chatId
1810
+ }, "loop pattern observation failed");
1811
+ });
1799
1812
  return {
1800
1813
  message: txResult.message,
1801
1814
  recipients: txResult.recipients
1802
1815
  };
1803
1816
  }
1817
+ function stripMentionPrefix(content) {
1818
+ return content.replace(/^(@\S+\s*)+/, "").trim();
1819
+ }
1820
+ const defaultLoopObserver = (data) => {
1821
+ log.warn({
1822
+ metric: "loop_pattern_observed_total",
1823
+ ...data
1824
+ }, "loop pattern observed (not blocked) — prompt discipline may be drifting");
1825
+ };
1826
+ /**
1827
+ * Pure observation: detect short, fast, two-agent ping-pong reply chains and
1828
+ * surface them via a structured log line. Does NOT modify the `notify` flag
1829
+ * or otherwise interfere with delivery — loop *prevention* lives client-side
1830
+ * (prompt + silent-turn protocol in `result-sink`). Six conjunctive
1831
+ * conditions (see design `agent-reply-loop-prevention-design.md` §3.4) so
1832
+ * normal multi-agent collaboration is never flagged:
1833
+ *
1834
+ * C1 — every message is `format=text`
1835
+ * C2 — no human sender in the window (any human reply resets the chain)
1836
+ * C3 — exactly two senders, perfectly alternating
1837
+ * C4 — strict `inReplyTo` chain across the whole window
1838
+ * C5 — every message body (after stripping leading `@<name>` tokens) is
1839
+ * ≤ `LOOP_OBSERVATION_SHORT_CHARS` characters
1840
+ * C6 — the whole window spans ≤ `LOOP_OBSERVATION_TIME_WINDOW_MS` ms
1841
+ *
1842
+ * Exported for direct test coverage of the detection logic; the `sendMessage`
1843
+ * call site uses the default observer.
1844
+ */
1845
+ async function observeLoopPattern(db, chatId, observer = defaultLoopObserver) {
1846
+ const window = await db.select({
1847
+ id: messages.id,
1848
+ senderId: messages.senderId,
1849
+ content: messages.content,
1850
+ inReplyTo: messages.inReplyTo,
1851
+ createdAt: messages.createdAt,
1852
+ format: messages.format,
1853
+ senderType: agents.type
1854
+ }).from(messages).innerJoin(agents, eq(messages.senderId, agents.uuid)).where(eq(messages.chatId, chatId)).orderBy(desc(messages.createdAt)).limit(4);
1855
+ if (window.length < 4) return;
1856
+ if (window.some((m) => m.format !== "text")) return;
1857
+ if (window.some((m) => m.senderType === "human")) return;
1858
+ if (new Set(window.map((m) => m.senderId)).size !== 2) return;
1859
+ for (let i = 0; i < window.length - 1; i++) if (window[i]?.senderId === window[i + 1]?.senderId) return;
1860
+ for (let i = 0; i < window.length - 1; i++) if (window[i]?.inReplyTo !== window[i + 1]?.id) return;
1861
+ const lens = [];
1862
+ for (const m of window) {
1863
+ const text = typeof m.content === "string" ? m.content : null;
1864
+ if (text === null) return;
1865
+ const len = stripMentionPrefix(text).length;
1866
+ if (len > 10) return;
1867
+ lens.push(len);
1868
+ }
1869
+ const newest = window[0];
1870
+ const oldest = window[window.length - 1];
1871
+ if (!newest || !oldest) return;
1872
+ const spanMs = newest.createdAt.getTime() - oldest.createdAt.getTime();
1873
+ if (spanMs > 3e4) return;
1874
+ observer({
1875
+ chatId,
1876
+ recentMessageIds: window.map((m) => m.id),
1877
+ windowSpanMs: spanMs,
1878
+ contentLengths: lens
1879
+ });
1880
+ }
1804
1881
  async function sendToAgent(db, senderUuid, targetName, data) {
1805
1882
  const [sender] = await db.select({
1806
1883
  uuid: agents.uuid,
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import "./observability-BAScT_5S-BcW9HgkG.mjs";
2
- import { A as checkDocker, B as isServiceSupported, E as checkAgentConfigs, F as checkWebSocket, G as uninstallClientService, H as restartClientService, I as printResults, J as stopPostgres, K as ensurePostgres, M as checkServerConfig, N as checkServerHealth, O as checkClientConfig, P as checkServerReachable, R as getClientServiceStatus, S as runHomeMigration, T as runMigrations, U as startClientService, V as resolveCliInvocation, W as stopClientService, X as handleClientOrgMismatch, Y as ClientRuntime, Z as rotateClientIdWithBackup, _ as formatCheckReport, b as onboardCreate, d as startServer, g as promptMissingFields, h as promptAddAgent, j as checkNodeVersion, k as checkDatabase, lt as FirstTreeHubSDK, m as isInteractive, n as deriveHubUrlFromToken, nt as hasUser, q as isDockerAvailable, t as HubUrlDerivationError, tt as createOwner, ut as SdkError, y as onboardCheck, z as installClientService } from "./saas-connect-CLcon-De.mjs";
2
+ import { A as checkDocker, B as isServiceSupported, E as checkAgentConfigs, F as checkWebSocket, G as uninstallClientService, H as restartClientService, I as printResults, J as stopPostgres, K as ensurePostgres, M as checkServerConfig, N as checkServerHealth, O as checkClientConfig, P as checkServerReachable, R as getClientServiceStatus, S as runHomeMigration, T as runMigrations, U as startClientService, V as resolveCliInvocation, W as stopClientService, X as handleClientOrgMismatch, Y as ClientRuntime, Z as rotateClientIdWithBackup, _ as formatCheckReport, b as onboardCreate, d as startServer, g as promptMissingFields, h as promptAddAgent, j as checkNodeVersion, k as checkDatabase, lt as FirstTreeHubSDK, m as isInteractive, n as deriveHubUrlFromToken, nt as hasUser, q as isDockerAvailable, t as HubUrlDerivationError, tt as createOwner, ut as SdkError, y as onboardCheck, z as installClientService } from "./saas-connect-CXZhK485.mjs";
3
3
  import "./logger-core-BTmvdflj-DjW8FM4T.mjs";
4
4
  import { a as ensureFreshAdminToken, c as resolveServerUrl, i as ensureFreshAccessToken, n as AuthRefreshRateLimitedError, s as resolveAccessToken, t as AuthRefreshFailedError } from "./bootstrap-BCZC1ki6.mjs";
5
5
  import { i as blank, s as status } from "./cli-fetch--tiwKm5S.mjs";
@@ -7,6 +7,6 @@ import "./dist-CnjqakXS.mjs";
7
7
  import { n as bindFeishuUser, t as bindFeishuBot } from "./feishu-DrnBbl8T.mjs";
8
8
  import "./errors-CF5evtJt-B0NTIVPt.mjs";
9
9
  import "./src-DNBS5Yjj.mjs";
10
- import "./client-bR8nwHaV-OxnjyKOk.mjs";
10
+ import "./client-OMwJMCQt-R1T06ZH6.mjs";
11
11
  import "./invitation-Bg0TRiyx-BsZH4GCS.mjs";
12
12
  export { AuthRefreshFailedError, AuthRefreshRateLimitedError, ClientRuntime, FirstTreeHubSDK, HubUrlDerivationError, SdkError, bindFeishuBot, bindFeishuUser, blank, checkAgentConfigs, checkClientConfig, checkDatabase, checkDocker, checkNodeVersion, checkServerConfig, checkServerHealth, checkServerReachable, checkWebSocket, createOwner, deriveHubUrlFromToken, ensureFreshAccessToken, ensureFreshAdminToken, ensurePostgres, formatCheckReport, getClientServiceStatus, handleClientOrgMismatch, hasUser, installClientService, isDockerAvailable, isInteractive, isServiceSupported, onboardCheck, onboardCreate, printResults, promptAddAgent, promptMissingFields, resolveAccessToken, resolveCliInvocation, resolveServerUrl, restartClientService, rotateClientIdWithBackup, runHomeMigration, runMigrations, startClientService, startServer, status, stopClientService, stopPostgres, uninstallClientService };
@@ -5,7 +5,7 @@ import { a as print, i as blank, n as CLI_USER_AGENT, r as COMMAND_VERSION, s as
5
5
  import { $ as patchOnboardingSchema, A as defaultRuntimeConfigPayload, B as inboxDeliverFrameSchema$1, C as createAdapterMappingSchema, Ct as updateOrganizationSchema, D as createMemberSchema, E as createMeChatSchema, F as githubCallbackQuerySchema, G as joinByInvitationSchema, H as isOrgSettingNamespace, I as githubDevCallbackQuerySchema, J as messageSourceSchema$1, K as listMeChatsQuerySchema, L as githubStartQuerySchema, M as dryRunAgentRuntimeConfigSchema, O as createOrgFromMeSchema, P as githubAppInstallationClaimBodySchema, Q as patchChatEngagementSchema, R as imageInlineContentSchema, S as createAdapterConfigSchema, St as updateMemberSchema, T as createChatSchema, U as isRedactedEnvValue, V as inboxPollQuerySchema, W as isReservedAgentName$1, X as onboardingEventSchema, Y as notificationQuerySchema, Z as paginationQuerySchema, _ as chatMetadataSchema$1, _t as updateAdapterConfigSchema, a as AGENT_VISIBILITY, at as safeRedirectPath, b as connectTokenExchangeSchema, bt as updateChatSchema, c as MENTION_REGEX, ct as sendMessageSchema, d as addMeChatParticipantsSchema, dt as sessionEventMessageSchema, f as addParticipantSchema, ft as sessionEventSchema$1, g as agentTypeSchema$1, gt as submitQuestionAnswerSchema, h as agentRuntimeConfigPayloadSchema$1, ht as stripCode, i as AGENT_STATUSES, it as runtimeStateMessageSchema, j as delegateFeishuUserSchema, l as ORG_SETTINGS_NAMESPACES$1, lt as sendToAgentSchema, m as agentPinnedMessageSchema$1, mt as sessionStateMessageSchema, n as AGENT_NAME_REGEX$1, nt as rebindAgentSchema, o as CHAT_ENGAGEMENT_STATUSES, p as agentBindRequestSchema, pt as sessionReconcileRequestSchema, q as loginSchema, r as AGENT_SELECTOR_HEADER$1, rt as refreshTokenSchema, st as selfServiceFeishuBotSchema, t as AGENT_BIND_REJECT_REASONS, u as WS_AUTH_FRAME_TIMEOUT_MS, ut as sessionCompletionMessageSchema, vt as updateAgentRuntimeConfigSchema, w as createAgentSchema, wt as wsAuthFrameSchema, x as contextTreeSnapshotSchema, xt as updateClientCapabilitiesSchema, y as clientRegisterSchema, yt as updateAgentSchema, z as inboxAckFrameSchema } from "./dist-CnjqakXS.mjs";
6
6
  import { a as ConflictError, c as UnauthorizedError, i as ClientUserMismatchError$1, l as organizations, n as BadRequestError, o as ForbiddenError, r as ClientOrgMismatchError$1, s as NotFoundError, t as AppError, u as users } from "./errors-CF5evtJt-B0NTIVPt.mjs";
7
7
  import { n as init_esm, r as trace, t as esm_exports } from "./esm-iadMkGbV.mjs";
8
- import { $ as notifyRecipients, A as getPresence, B as listAgentsManagedByUser, C as ensureParticipant, D as getChatDetail, E as getCachedAudience, F as joinAsParticipant, G as listClients, H as listChatParticipantsWithNames, I as joinChat, K as listClientsForOrgAdmin, L as leaveAsParticipant, M as heartbeatInstance, N as inboxEntries, O as getClient, P as invalidateChatAudience, Q as messages, R as leaveChat, S as ensureCanJoin, T as getActivityOverview, U as listChats, V as listAgentsWithRuntime, W as listChatsForMember, X as markSupersededByChat, Y as markStaleAgents, Z as members, _ as createChat, _t as unbindAgent, a as agentVisibilityCondition, at as registerClient, b as disconnectClient, c as assertParticipant, ct as resolveChatMembership, d as chatMembership, dt as sendToAgent$1, et as pendingQuestions, f as chats, ft as serverInstances, g as clients, gt as touchAgent, h as cleanupStalePresence, ht as submitAnswer, i as agentPresence, it as registerChatMessageDispatcher, j as heartbeatClient, k as getOnlineCount, l as bindAgent, lt as retireClient, m as cleanupStaleClients, mt as setRuntimeState, n as addParticipant, nt as recomputeWatchersForAgent, o as agents, ot as removeParticipant, p as claimClient, pt as setOffline, q as listMessages, r as agentChatSessions, rt as recomputeWatchersForMember, s as assertClientOwner, st as resetActivity, t as addChatParticipants, tt as recomputeChatWatchers, u as changeChatType, ut as sendMessage, v as createNotifier, vt as updateClientCapabilities, w as findOrCreateDirectChat, x as editMessage, y as deriveAuthState, yt as upsertSessionState, z as listActiveAgentsPinnedToClient } from "./client-bR8nwHaV-OxnjyKOk.mjs";
8
+ import { $ as notifyRecipients, A as getPresence, B as listAgentsManagedByUser, C as ensureParticipant, D as getChatDetail, E as getCachedAudience, F as joinAsParticipant, G as listClients, H as listChatParticipantsWithNames, I as joinChat, K as listClientsForOrgAdmin, L as leaveAsParticipant, M as heartbeatInstance, N as inboxEntries, O as getClient, P as invalidateChatAudience, Q as messages, R as leaveChat, S as ensureCanJoin, T as getActivityOverview, U as listChats, V as listAgentsWithRuntime, W as listChatsForMember, X as markSupersededByChat, Y as markStaleAgents, Z as members, _ as createChat, _t as unbindAgent, a as agentVisibilityCondition, at as registerClient, b as disconnectClient, c as assertParticipant, ct as resolveChatMembership, d as chatMembership, dt as sendToAgent$1, et as pendingQuestions, f as chats, ft as serverInstances, g as clients, gt as touchAgent, h as cleanupStalePresence, ht as submitAnswer, i as agentPresence, it as registerChatMessageDispatcher, j as heartbeatClient, k as getOnlineCount, l as bindAgent, lt as retireClient, m as cleanupStaleClients, mt as setRuntimeState, n as addParticipant, nt as recomputeWatchersForAgent, o as agents, ot as removeParticipant, p as claimClient, pt as setOffline, q as listMessages, r as agentChatSessions, rt as recomputeWatchersForMember, s as assertClientOwner, st as resetActivity, t as addChatParticipants, tt as recomputeChatWatchers, u as changeChatType, ut as sendMessage, v as createNotifier, vt as updateClientCapabilities, w as findOrCreateDirectChat, x as editMessage, y as deriveAuthState, yt as upsertSessionState, z as listActiveAgentsPinnedToClient } from "./client-OMwJMCQt-R1T06ZH6.mjs";
9
9
  import { a as invitationRedemptions, c as recordRedemption, i as getActiveInvitation, l as rotateInvitation, n as ensureActiveInvitation, o as invitations, r as findActiveByToken, t as buildInviteUrl, u as uuidv7 } from "./invitation-Bg0TRiyx-BsZH4GCS.mjs";
10
10
  import { createRequire } from "node:module";
11
11
  import { ZodError, z } from "zod";
@@ -3254,7 +3254,9 @@ You are running inside **Agent Hub**, a messaging platform for agent teams.
3254
3254
  - **Your final text response is automatically delivered** to the chat — just respond normally
3255
3255
  - For **proactive communication** (sending to other agents, other chats, or structured data),
3256
3256
  use the \`first-tree-hub\` CLI below
3257
- - **Use your judgment about when to respond.** Not every message requires a reply.
3257
+ - **Use your judgment about when to respond.** Not every message requires
3258
+ a reply — if you have nothing new for the recipient, output nothing and
3259
+ the runtime will end the turn silently.
3258
3260
  Your role and responsibilities are injected via the Hub-managed system prompt.
3259
3261
 
3260
3262
  ## Environment Variables
@@ -5858,6 +5860,11 @@ function createResultSink(deps) {
5858
5860
  return { mentions: [trigger.senderId] };
5859
5861
  }
5860
5862
  return async function forwardResult(text) {
5863
+ if (text.trim().length === 0) {
5864
+ deps.clearTrigger();
5865
+ deps.log("silent turn: agent produced empty output, skipping delivery");
5866
+ return;
5867
+ }
5861
5868
  const trigger = deps.getTrigger();
5862
5869
  deps.clearTrigger();
5863
5870
  const metadata = await buildMetadata(trigger);
@@ -10649,7 +10656,7 @@ function createFeedbackHandler(config) {
10649
10656
  return { handle };
10650
10657
  }
10651
10658
  //#endregion
10652
- //#region ../server/dist/app-mkBHfGPl.mjs
10659
+ //#region ../server/dist/app-BpskScln.mjs
10653
10660
  var import_fastify_opentelemetry = /* @__PURE__ */ __toESM(require_fastify_opentelemetry(), 1);
10654
10661
  init_esm();
10655
10662
  var __defProp = Object.defineProperty;
@@ -17666,7 +17673,7 @@ async function meRoutes(app) {
17666
17673
  */
17667
17674
  app.get("/me/pinned-agents", async (request) => {
17668
17675
  const { userId } = requireUser(request);
17669
- const { listMyPinnedAgents } = await import("./client-DNEtPEBu-BtHkUya2.mjs");
17676
+ const { listMyPinnedAgents } = await import("./client-CjGIGddS-BrpazWa3.mjs");
17670
17677
  return listMyPinnedAgents(app.db, { userId });
17671
17678
  });
17672
17679
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-team-foundation/first-tree-hub",
3
- "version": "0.12.8",
3
+ "version": "0.12.9",
4
4
  "type": "module",
5
5
  "description": "First Tree Hub — unified CLI for server, client, and agent management",
6
6
  "exports": {