@vellumai/assistant 0.3.14 → 0.3.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/Dockerfile CHANGED
@@ -1,5 +1,5 @@
1
1
  # Use debian as base image
2
- FROM us-central1-docker.pkg.dev/code-executor-service/base-images/debian:bookworm@sha256:940221f8c2564efc4e7909caba7014dc8bcba7f05b7881eb2b557b4058c41269 AS builder
2
+ FROM debian:bookworm AS builder
3
3
 
4
4
  WORKDIR /app
5
5
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vellumai/assistant",
3
- "version": "0.3.14",
3
+ "version": "0.3.15",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "vellum": "./src/index.ts"
@@ -1,7 +1,5 @@
1
- import { afterAll, afterEach, beforeEach, describe, expect, mock, test } from 'bun:test';
1
+ import { afterAll, beforeEach, describe, expect, mock, test } from 'bun:test';
2
2
 
3
- import type { PolicyContext } from '../permissions/types.js';
4
- import { RiskLevel } from '../permissions/types.js';
5
3
  import type { ToolExecutionResult, ToolLifecycleEvent, ToolPermissionDeniedEvent } from '../tools/types.js';
6
4
 
7
5
  // ── Module mocks (must precede real imports) ─────────────────────────
@@ -92,20 +92,20 @@ globalThis.fetch = (async (input: string | URL | Request, init?: RequestInit) =>
92
92
  // Now import modules under test (after mocks are in place)
93
93
  // ---------------------------------------------------------------------------
94
94
 
95
+ import { getDb, initializeDb, resetDb } from '../memory/db.js';
96
+ import {
97
+ updateSessionDelivery,
98
+ } from '../runtime/channel-guardian-service.js';
95
99
  import {
96
- startOutbound,
97
- resendOutbound,
98
100
  cancelOutbound,
101
+ resendOutbound,
102
+ startOutbound,
99
103
  } from '../runtime/guardian-outbound-actions.js';
100
104
  import {
101
- handleStartOutbound,
102
- handleResendOutbound,
103
105
  handleCancelOutbound,
106
+ handleResendOutbound,
107
+ handleStartOutbound,
104
108
  } from '../runtime/routes/integration-routes.js';
105
- import {
106
- updateSessionDelivery,
107
- } from '../runtime/channel-guardian-service.js';
108
- import { getDb, initializeDb, resetDb } from '../memory/db.js';
109
109
 
110
110
  // Initialize the database (creates all tables)
111
111
  initializeDb();
@@ -18,7 +18,6 @@ mock.module('../util/logger.js', () => ({
18
18
  }));
19
19
 
20
20
  import { enforceRoutingIntent } from '../notifications/decision-engine.js';
21
- import type { RoutingIntent } from '../notifications/signal.js';
22
21
  import type { NotificationChannel, NotificationDecision } from '../notifications/types.js';
23
22
 
24
23
  // -- Helpers -----------------------------------------------------------------
@@ -32,7 +32,6 @@ mock.module('../util/logger.js', () => ({
32
32
  import {
33
33
  classifyRecordingIntentFallback,
34
34
  containsRecordingKeywords,
35
- type RecordingFallbackResult,
36
35
  } from '../daemon/recording-intent-fallback.js';
37
36
 
38
37
  beforeEach(() => {
@@ -83,6 +83,7 @@ let mockIntentResult: RecordingIntentResult = { kind: 'none' };
83
83
  // Capture real function references BEFORE mock.module replaces the module.
84
84
  // require() at this point returns the real module since mock.module has not
85
85
  // been called yet for this specifier.
86
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
86
87
  const _realRecordingIntentMod = require('../daemon/recording-intent.js');
87
88
  const _realResolveRecordingIntent = _realRecordingIntentMod.resolveRecordingIntent;
88
89
  const _realStripDynamicNames = _realRecordingIntentMod.stripDynamicNames;
@@ -125,6 +126,7 @@ let executorCalled = false;
125
126
 
126
127
  let _realExecuteRecordingIntent: ((...args: any[]) => any) | null = null;
127
128
  try {
129
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
128
130
  const _mod = require('../daemon/recording-executor.js');
129
131
  _realExecuteRecordingIntent = _mod.executeRecordingIntent;
130
132
  } catch {
@@ -152,7 +154,7 @@ mock.module('../daemon/recording-executor.js', () => ({
152
154
  // handlers/recording.js, it gets the real behavior.
153
155
 
154
156
  let recordingStartCalled = false;
155
- let recordingStopCalled = false;
157
+ let _recordingStopCalled = false;
156
158
  let recordingRestartCalled = false;
157
159
  let recordingPauseCalled = false;
158
160
  let recordingResumeCalled = false;
@@ -167,6 +169,7 @@ let _realRecordingHandlers: any = {};
167
169
  let _realResetRecordingState: ((...args: any[]) => any) | null = null;
168
170
 
169
171
  try {
172
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
170
173
  const _mod = require('../daemon/handlers/recording.js');
171
174
  _realHandleRecordingStart = _mod.handleRecordingStart;
172
175
  _realHandleRecordingStop = _mod.handleRecordingStop;
@@ -190,7 +193,7 @@ mock.module('../daemon/handlers/recording.js', () => ({
190
193
  },
191
194
  handleRecordingStop: (...args: any[]) => {
192
195
  if ((globalThis as any).__riHandlerUseMockIntent) {
193
- recordingStopCalled = true;
196
+ _recordingStopCalled = true;
194
197
  return 'mock-recording-id';
195
198
  }
196
199
  return _realHandleRecordingStop?.(...args);
@@ -420,7 +423,7 @@ function resetMockState(): void {
420
423
  mockExecuteResult = { handled: false };
421
424
  mockAssistantName = null;
422
425
  recordingStartCalled = false;
423
- recordingStopCalled = false;
426
+ _recordingStopCalled = false;
424
427
  recordingRestartCalled = false;
425
428
  recordingPauseCalled = false;
426
429
  recordingResumeCalled = false;
@@ -1,7 +1,7 @@
1
- import { describe, test, expect, mock, beforeEach } from 'bun:test';
1
+ import { beforeEach,describe, expect, mock, test } from 'bun:test';
2
+
2
3
  import {
3
4
  resolveRecordingIntent,
4
- type RecordingIntentResult,
5
5
  } from '../daemon/recording-intent.js';
6
6
 
7
7
  // ─── resolveRecordingIntent ─────────────────────────────────────────────────
@@ -1,7 +1,7 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
- import { describe, test, expect, beforeEach, mock } from 'bun:test';
3
1
  import * as net from 'node:net';
4
2
 
3
+ import { beforeEach, describe, expect, mock,test } from 'bun:test';
4
+
5
5
  // ─── Mocks (must be before any imports that depend on them) ─────────────────
6
6
 
7
7
  const noop = () => {};
@@ -66,8 +66,8 @@ mock.module('../memory/attachments-store.js', () => ({
66
66
  }));
67
67
 
68
68
  // Mock node:fs
69
- mock.module('node:fs', () => {
70
- const realFs = require('fs');
69
+ mock.module('node:fs', async () => {
70
+ const realFs = await import('fs');
71
71
  return {
72
72
  ...realFs,
73
73
  existsSync: (p: string) => {
@@ -89,19 +89,19 @@ mock.module('../daemon/video-thumbnail.js', () => ({
89
89
  // ─── Imports (after mocks) ──────────────────────────────────────────────────
90
90
 
91
91
  import {
92
- handleRecordingStart,
93
- handleRecordingStop,
94
- handleRecordingRestart,
92
+ __resetRecordingState,
93
+ getActiveRestartToken,
95
94
  handleRecordingPause,
95
+ handleRecordingRestart,
96
96
  handleRecordingResume,
97
+ handleRecordingStart,
98
+ handleRecordingStop,
97
99
  isRecordingIdle,
98
- getActiveRestartToken,
99
100
  recordingHandlers,
100
- __resetRecordingState,
101
101
  } from '../daemon/handlers/recording.js';
102
- import { executeRecordingIntent } from '../daemon/recording-executor.js';
103
102
  import type { HandlerContext } from '../daemon/handlers/shared.js';
104
103
  import type { RecordingStatus } from '../daemon/ipc-contract/computer-use.js';
104
+ import { executeRecordingIntent } from '../daemon/recording-executor.js';
105
105
  import { DebouncerMap } from '../util/debounce.js';
106
106
 
107
107
  // ─── Test helpers ───────────────────────────────────────────────────────────
@@ -996,7 +996,7 @@ describe('deferred restart prevents race condition', () => {
996
996
 
997
997
  test('stop-ack timeout cleans up deferred restart state', () => {
998
998
  // This test uses a real timer via bun's jest-compatible API
999
- const { ctx, sent, fakeSocket } = createCtx();
999
+ const { ctx, fakeSocket } = createCtx();
1000
1000
  const conversationId = 'conv-deferred-timeout';
1001
1001
  ctx.socketToSession.set(fakeSocket, conversationId);
1002
1002
 
@@ -1,22 +1,22 @@
1
1
  import * as net from 'node:net';
2
2
 
3
+ import type { ChannelId } from '../../channels/types.js';
3
4
  import * as externalConversationStore from '../../memory/external-conversation-store.js';
4
5
  import {
5
6
  createVerificationChallenge,
7
+ findActiveSession,
6
8
  getGuardianBinding,
7
9
  getPendingChallenge,
8
10
  revokeBinding as revokeGuardianBinding,
9
11
  revokePendingChallenges,
10
- findActiveSession,
11
12
  } from '../../runtime/channel-guardian-service.js';
12
13
  import { type ChannelReadinessService, createReadinessService } from '../../runtime/channel-readiness-service.js';
13
14
  import {
14
- startOutbound,
15
- resendOutbound,
16
15
  cancelOutbound,
16
+ resendOutbound,
17
+ startOutbound,
17
18
  } from '../../runtime/guardian-outbound-actions.js';
18
19
  import { normalizeAssistantId } from '../../util/platform.js';
19
- import type { ChannelId } from '../../channels/types.js';
20
20
  import type {
21
21
  ChannelReadinessRequest,
22
22
  GuardianVerificationRequest,
@@ -34,10 +34,10 @@ export type GuardianVerificationResult = Omit<GuardianVerificationResponse, 'typ
34
34
  // ---------------------------------------------------------------------------
35
35
 
36
36
  export {
37
+ DESTINATION_RATE_WINDOW_MS,
38
+ MAX_SENDS_PER_DESTINATION_WINDOW,
37
39
  MAX_SENDS_PER_SESSION,
38
40
  RESEND_COOLDOWN_MS,
39
- MAX_SENDS_PER_DESTINATION_WINDOW,
40
- DESTINATION_RATE_WINDOW_MS,
41
41
  } from '../../runtime/guardian-outbound-actions.js';
42
42
 
43
43
  // ---------------------------------------------------------------------------
@@ -1,6 +1,6 @@
1
1
  import * as net from 'node:net';
2
2
 
3
- import { recordConversationSeenSignal, type Confidence, type SignalType } from '../../memory/conversation-attention-store.js';
3
+ import { type Confidence, recordConversationSeenSignal, type SignalType } from '../../memory/conversation-attention-store.js';
4
4
  import { updateDeliveryClientOutcome } from '../../notifications/deliveries-store.js';
5
5
  import type { ClientMessage } from '../ipc-protocol.js';
6
6
  import { handleRideShotgunStart, handleRideShotgunStop } from '../ride-shotgun-handler.js';
@@ -22,8 +22,8 @@ import type {
22
22
  TaskSubmit,
23
23
  } from '../ipc-protocol.js';
24
24
  import { executeRecordingIntent } from '../recording-executor.js';
25
- import { classifyRecordingIntentFallback, containsRecordingKeywords } from '../recording-intent-fallback.js';
26
25
  import { resolveRecordingIntent } from '../recording-intent.js';
26
+ import { classifyRecordingIntentFallback, containsRecordingKeywords } from '../recording-intent-fallback.js';
27
27
  import { buildSessionErrorMessage,classifySessionError } from '../session-error.js';
28
28
  import { handleCuSessionCreate } from './computer-use.js';
29
29
  import { handleRecordingPause, handleRecordingRestart, handleRecordingResume, handleRecordingStart, handleRecordingStop, isRecordingIdle } from './recording.js';
@@ -36,8 +36,8 @@ import type {
36
36
  } from '../ipc-protocol.js';
37
37
  import { normalizeThreadType } from '../ipc-protocol.js';
38
38
  import { executeRecordingIntent } from '../recording-executor.js';
39
- import { classifyRecordingIntentFallback, containsRecordingKeywords } from '../recording-intent-fallback.js';
40
39
  import { resolveRecordingIntent } from '../recording-intent.js';
40
+ import { classifyRecordingIntentFallback, containsRecordingKeywords } from '../recording-intent-fallback.js';
41
41
  import { buildSessionErrorMessage,classifySessionError } from '../session-error.js';
42
42
  import { generateVideoThumbnail } from '../video-thumbnail.js';
43
43
  import { handleRecordingPause, handleRecordingRestart, handleRecordingResume, handleRecordingStart, handleRecordingStop } from './recording.js';
@@ -479,12 +479,12 @@ export function handleSessionList(socket: net.Socket, ctx: HandlerContext, offse
479
479
  const originInterface = parseInterfaceId(c.originInterface);
480
480
  const attn = attentionStates.get(c.id);
481
481
  const assistantAttention = attn ? {
482
- hasUnseenLatestAssistantMessage: attn.latestAssistantMessageAt !== null &&
483
- (attn.lastSeenAssistantMessageAt === null || attn.lastSeenAssistantMessageAt < attn.latestAssistantMessageAt),
484
- ...(attn.latestAssistantMessageAt !== null ? { latestAssistantMessageAt: attn.latestAssistantMessageAt } : {}),
485
- ...(attn.lastSeenAssistantMessageAt !== null ? { lastSeenAssistantMessageAt: attn.lastSeenAssistantMessageAt } : {}),
486
- ...(attn.lastSeenConfidence !== null ? { lastSeenConfidence: attn.lastSeenConfidence } : {}),
487
- ...(attn.lastSeenSignalType !== null ? { lastSeenSignalType: attn.lastSeenSignalType } : {}),
482
+ hasUnseenLatestAssistantMessage: attn.latestAssistantMessageAt != null &&
483
+ (attn.lastSeenAssistantMessageAt == null || attn.lastSeenAssistantMessageAt < attn.latestAssistantMessageAt),
484
+ ...(attn.latestAssistantMessageAt != null ? { latestAssistantMessageAt: attn.latestAssistantMessageAt } : {}),
485
+ ...(attn.lastSeenAssistantMessageAt != null ? { lastSeenAssistantMessageAt: attn.lastSeenAssistantMessageAt } : {}),
486
+ ...(attn.lastSeenConfidence != null ? { lastSeenConfidence: attn.lastSeenConfidence } : {}),
487
+ ...(attn.lastSeenSignalType != null ? { lastSeenSignalType: attn.lastSeenSignalType } : {}),
488
488
  } : undefined;
489
489
  return {
490
490
  id: c.id,
@@ -5,7 +5,6 @@
5
5
 
6
6
  import type * as net from 'node:net';
7
7
 
8
- import type { HandlerContext } from './handlers/shared.js';
9
8
  import {
10
9
  handleRecordingPause,
11
10
  handleRecordingRestart,
@@ -14,6 +13,7 @@ import {
14
13
  handleRecordingStop,
15
14
  isRecordingIdle,
16
15
  } from './handlers/recording.js';
16
+ import type { HandlerContext } from './handlers/shared.js';
17
17
  import type { RecordingIntentResult } from './recording-intent.js';
18
18
 
19
19
  export interface RecordingExecutionContext {
@@ -383,7 +383,7 @@ function isInterrogative(text: string, dynamicNames?: string[]): boolean {
383
383
  * If `dynamicNames` are provided, they are stripped from the beginning of the
384
384
  * text before classification (e.g., "Nova, record my screen" -> "record my screen").
385
385
  */
386
- function classifyRecordingIntent(
386
+ function _classifyRecordingIntent(
387
387
  taskText: string,
388
388
  dynamicNames?: string[],
389
389
  ): RecordingIntentClass {
@@ -25,8 +25,8 @@ import { requestComputerControlTool } from '../tools/computer-use/request-comput
25
25
  import type { ProxyApprovalCallback, ProxyApprovalRequest } from '../tools/network/script-proxy/index.js';
26
26
  import { getAllToolDefinitions } from '../tools/registry.js';
27
27
  import { allUiSurfaceTools } from '../tools/ui-surface/definitions.js';
28
- import { projectSkillTools, type SkillProjectionCache } from './session-skill-tools.js';
29
28
  import type { GuardianRuntimeContext } from './session-runtime-assembly.js';
29
+ import { projectSkillTools, type SkillProjectionCache } from './session-skill-tools.js';
30
30
  import type { SurfaceSessionContext } from './session-surfaces.js';
31
31
  import {
32
32
  surfaceProxyResolver,
@@ -13,7 +13,7 @@ import { getLogger } from '../util/logger.js';
13
13
  import { getDb } from './db.js';
14
14
  import { conversationAssistantAttentionState, conversationAttentionEvents, conversations, messages } from './schema.js';
15
15
 
16
- const log = getLogger('conversation-attention-store');
16
+ const _log = getLogger('conversation-attention-store');
17
17
 
18
18
  // ── Types ────────────────────────────────────────────────────────────
19
19
 
@@ -138,7 +138,7 @@ export function projectAssistantMessage(params: {
138
138
  }
139
139
 
140
140
  // Monotonic: only advance if the new message is strictly later
141
- if (existing.latestAssistantMessageAt !== null && messageAt <= existing.latestAssistantMessageAt) {
141
+ if (existing.latestAssistantMessageAt != null && messageAt <= existing.latestAssistantMessageAt) {
142
142
  return;
143
143
  }
144
144
 
@@ -251,8 +251,8 @@ export function recordConversationSeenSignal(params: {
251
251
  // Only advance the seen cursor if there is a latest assistant message to mark as seen,
252
252
  // and the seen cursor hasn't already reached or passed it (monotonic invariant).
253
253
  const shouldAdvanceSeen =
254
- state.latestAssistantMessageAt !== null &&
255
- (state.lastSeenAssistantMessageAt === null ||
254
+ state.latestAssistantMessageAt != null &&
255
+ (state.lastSeenAssistantMessageAt == null ||
256
256
  state.latestAssistantMessageAt > state.lastSeenAssistantMessageAt);
257
257
 
258
258
  // Guard seen metadata monotonicity: only update lastSeen* metadata when the
@@ -260,7 +260,7 @@ export function recordConversationSeenSignal(params: {
260
260
  // Out-of-order delivery (e.g. delayed channel callbacks) must not regress
261
261
  // the projected channel/source/confidence metadata.
262
262
  const isNewerSignal =
263
- state.lastSeenEventAt === null || eventObservedAt >= state.lastSeenEventAt;
263
+ state.lastSeenEventAt == null || eventObservedAt >= state.lastSeenEventAt;
264
264
 
265
265
  const updates: Record<string, unknown> = {
266
266
  updatedAt: now,
@@ -7,8 +7,16 @@
7
7
  * IPC handler (config-channels.ts) and the HTTP route layer (integration-routes.ts).
8
8
  */
9
9
 
10
- import { randomBytes, createHash } from 'node:crypto';
10
+ import { createHash,randomBytes } from 'node:crypto';
11
11
 
12
+ import { startGuardianVerificationCall } from '../calls/call-domain.js';
13
+ import type { ChannelId } from '../channels/types.js';
14
+ import { getGatewayInternalBaseUrl } from '../config/env.js';
15
+ import { sendMessage as sendSms } from '../messaging/providers/sms/client.js';
16
+ import { getCredentialMetadata } from '../tools/credentials/metadata-store.js';
17
+ import { getLogger } from '../util/logger.js';
18
+ import { normalizePhoneNumber } from '../util/phone.js';
19
+ import { normalizeAssistantId, readHttpToken } from '../util/platform.js';
12
20
  import {
13
21
  countRecentSendsToDestination,
14
22
  createOutboundSession,
@@ -22,14 +30,6 @@ import {
22
30
  composeVerificationTelegram,
23
31
  GUARDIAN_VERIFY_TEMPLATE_KEYS,
24
32
  } from './guardian-verification-templates.js';
25
- import { startGuardianVerificationCall } from '../calls/call-domain.js';
26
- import { sendMessage as sendSms } from '../messaging/providers/sms/client.js';
27
- import { getGatewayInternalBaseUrl } from '../config/env.js';
28
- import { getCredentialMetadata } from '../tools/credentials/metadata-store.js';
29
- import { normalizePhoneNumber } from '../util/phone.js';
30
- import { normalizeAssistantId, readHttpToken } from '../util/platform.js';
31
- import { getLogger } from '../util/logger.js';
32
- import type { ChannelId } from '../channels/types.js';
33
33
 
34
34
  const log = getLogger('guardian-outbound-actions');
35
35
 
@@ -29,7 +29,7 @@ import {
29
29
  } from '../config/env.js';
30
30
  import type { ServerMessage } from '../daemon/ipc-contract.js';
31
31
  import { PairingStore } from '../daemon/pairing-store.js';
32
- import { type Confidence, type SignalType, getAttentionStateByConversationIds, recordConversationSeenSignal } from '../memory/conversation-attention-store.js';
32
+ import { type Confidence, getAttentionStateByConversationIds, recordConversationSeenSignal,type SignalType } from '../memory/conversation-attention-store.js';
33
33
  import * as conversationStore from '../memory/conversation-store.js';
34
34
  import * as externalConversationStore from '../memory/external-conversation-store.js';
35
35
  import { consumeCallback, consumeCallbackError } from '../security/oauth-callback-registry.js';
@@ -549,12 +549,12 @@ export class RuntimeHttpServer {
549
549
  const originChannel = parseChannelId(c.originChannel);
550
550
  const attn = attentionStates.get(c.id);
551
551
  const assistantAttention = attn ? {
552
- hasUnseenLatestAssistantMessage: attn.latestAssistantMessageAt !== null &&
553
- (attn.lastSeenAssistantMessageAt === null || attn.lastSeenAssistantMessageAt < attn.latestAssistantMessageAt),
554
- ...(attn.latestAssistantMessageAt !== null ? { latestAssistantMessageAt: attn.latestAssistantMessageAt } : {}),
555
- ...(attn.lastSeenAssistantMessageAt !== null ? { lastSeenAssistantMessageAt: attn.lastSeenAssistantMessageAt } : {}),
556
- ...(attn.lastSeenConfidence !== null ? { lastSeenConfidence: attn.lastSeenConfidence } : {}),
557
- ...(attn.lastSeenSignalType !== null ? { lastSeenSignalType: attn.lastSeenSignalType } : {}),
552
+ hasUnseenLatestAssistantMessage: attn.latestAssistantMessageAt != null &&
553
+ (attn.lastSeenAssistantMessageAt == null || attn.lastSeenAssistantMessageAt < attn.latestAssistantMessageAt),
554
+ ...(attn.latestAssistantMessageAt != null ? { latestAssistantMessageAt: attn.latestAssistantMessageAt } : {}),
555
+ ...(attn.lastSeenAssistantMessageAt != null ? { lastSeenAssistantMessageAt: attn.lastSeenAssistantMessageAt } : {}),
556
+ ...(attn.lastSeenConfidence != null ? { lastSeenConfidence: attn.lastSeenConfidence } : {}),
557
+ ...(attn.lastSeenSignalType != null ? { lastSeenSignalType: attn.lastSeenSignalType } : {}),
558
558
  } : undefined;
559
559
  return {
560
560
  id: c.id,
@@ -61,9 +61,9 @@ export function handleListConversationAttention(url: URL): Response {
61
61
  const results = pageStates.map((attn) => {
62
62
  const conv = conversationMap.get(attn.conversationId);
63
63
  const convSource = conv?.source ?? 'user';
64
- const hasUnseen = attn.latestAssistantMessageAt !== null &&
65
- (attn.lastSeenAssistantMessageAt === null || attn.lastSeenAssistantMessageAt < attn.latestAssistantMessageAt);
66
- const state: 'seen' | 'unseen' | 'no_assistant_message' = attn.latestAssistantMessageAt === null
64
+ const hasUnseen = attn.latestAssistantMessageAt != null &&
65
+ (attn.lastSeenAssistantMessageAt == null || attn.lastSeenAssistantMessageAt < attn.latestAssistantMessageAt);
66
+ const state: 'seen' | 'unseen' | 'no_assistant_message' = attn.latestAssistantMessageAt == null
67
67
  ? 'no_assistant_message'
68
68
  : hasUnseen ? 'unseen' : 'seen';
69
69
 
@@ -21,11 +21,6 @@ import {
21
21
  createGuardianChallenge,
22
22
  getGuardianStatus,
23
23
  } from '../../daemon/handlers/config-channels.js';
24
- import {
25
- startOutbound,
26
- resendOutbound,
27
- cancelOutbound,
28
- } from '../guardian-outbound-actions.js';
29
24
  import {
30
25
  clearTelegramConfig,
31
26
  getTelegramConfig,
@@ -33,6 +28,11 @@ import {
33
28
  setTelegramConfig,
34
29
  setupTelegram,
35
30
  } from '../../daemon/handlers/config-telegram.js';
31
+ import {
32
+ cancelOutbound,
33
+ resendOutbound,
34
+ startOutbound,
35
+ } from '../guardian-outbound-actions.js';
36
36
 
37
37
  /**
38
38
  * GET /v1/integrations/telegram/config
@@ -2,7 +2,7 @@ import { createConversation } from '../memory/conversation-store.js';
2
2
  import { GENERATING_TITLE, queueGenerateConversationTitle } from '../memory/conversation-title-service.js';
3
3
  import { invalidateAssistantInferredItemsForConversation } from '../memory/task-memory-cleanup.js';
4
4
  import { runSequencesOnce } from '../sequence/engine.js';
5
- import { claimDueReminders, completeReminder, failReminder, setReminderConversationId, type RoutingIntent } from '../tools/reminder/reminder-store.js';
5
+ import { claimDueReminders, completeReminder, failReminder, type RoutingIntent,setReminderConversationId } from '../tools/reminder/reminder-store.js';
6
6
  import { getLogger } from '../util/logger.js';
7
7
  import { runWatchersOnce, type WatcherEscalator,type WatcherNotifier } from '../watcher/engine.js';
8
8
  import { hasSetConstructs } from './recurrence-engine.js';
@@ -15,8 +15,8 @@ import { PermissionDeniedError,ToolError } from '../util/errors.js';
15
15
  import { pathExists, safeStatSync } from '../util/fs.js';
16
16
  import { getLogger } from '../util/logger.js';
17
17
  import { resolveExecutionTarget } from './execution-target.js';
18
- import { enforceGuardianOnlyPolicy } from './guardian-control-plane-policy.js';
19
18
  import { executeWithTimeout,safeTimeoutMs } from './execution-timeout.js';
19
+ import { enforceGuardianOnlyPolicy } from './guardian-control-plane-policy.js';
20
20
  import { buildPolicyContext } from './policy-context.js';
21
21
  import { getAllTools,getTool } from './registry.js';
22
22
  import { applyEdit } from './shared/filesystem/edit-engine.js';