@teamvibe/poller 0.7.2 → 0.8.1
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/dist/auto-update.js +11 -4
- package/dist/claude-spawner.d.ts +1 -0
- package/dist/claude-spawner.js +48 -11
- package/dist/config.js +6 -2
- package/dist/poller.js +9 -1
- package/npm-shrinkwrap.json +1606 -0
- package/package.json +13 -6
package/dist/auto-update.js
CHANGED
|
@@ -72,10 +72,17 @@ export async function checkForUpdate() {
|
|
|
72
72
|
logger.info(`[AutoUpdate] Already on latest version (${POLLER_VERSION})`);
|
|
73
73
|
return false;
|
|
74
74
|
}
|
|
75
|
-
// Check if update type is within policy
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
75
|
+
// Check if update type is within policy. Explicit 'off' branch (tv#312):
|
|
76
|
+
// startAutoUpdateCron() never schedules this job at all when policy is
|
|
77
|
+
// 'off', so this path is currently unreachable in practice — kept
|
|
78
|
+
// explicit so a future caller of checkForUpdate() outside the cron
|
|
79
|
+
// (manual trigger, control message) can't silently apply patch updates
|
|
80
|
+
// to a poller configured not to auto-update.
|
|
81
|
+
const policyAllows = config.AUTO_UPDATE_POLICY === 'off'
|
|
82
|
+
? false
|
|
83
|
+
: config.AUTO_UPDATE_POLICY === 'minor'
|
|
84
|
+
? updateType === 'patch' || updateType === 'minor'
|
|
85
|
+
: updateType === 'patch'; // policy === 'patch'
|
|
79
86
|
if (!policyAllows) {
|
|
80
87
|
logger.info(`[AutoUpdate] ${updateType} update available (${POLLER_VERSION} → ${latestVersion}) but policy is "${config.AUTO_UPDATE_POLICY}", skipping`);
|
|
81
88
|
return false;
|
package/dist/claude-spawner.d.ts
CHANGED
|
@@ -212,6 +212,7 @@ export interface SpawnStdinPlan {
|
|
|
212
212
|
export declare function buildSpawnStdinPlan(prompt: string, inlineImages: TeamVibeQueueMessage['inlineImages'], now?: number): SpawnStdinPlan;
|
|
213
213
|
export declare function extractAssistantText(event: any): string | undefined;
|
|
214
214
|
export declare function assistantCalledTool(event: any, toolName: string): boolean;
|
|
215
|
+
export declare function resolveTypingStatus(event: any, assistantText?: string): string | null;
|
|
215
216
|
/**
|
|
216
217
|
* Decides whether a spawn attempt should report its session ID back to the
|
|
217
218
|
* caller (poller.ts) for persistence to DynamoDB via updateSessionId().
|
package/dist/claude-spawner.js
CHANGED
|
@@ -699,6 +699,50 @@ export function assistantCalledTool(event, toolName) {
|
|
|
699
699
|
}
|
|
700
700
|
return event.message.content.some((c) => c?.type === 'tool_use' && c.name === toolName);
|
|
701
701
|
}
|
|
702
|
+
// Status text is a one-line UI label, not a chat message — cap it well short
|
|
703
|
+
// of a full sentence.
|
|
704
|
+
const TYPING_STATUS_MAX_LEN = 100;
|
|
705
|
+
// tv#369: decide what the typing indicator should show for a given
|
|
706
|
+
// stream-json event. Prefers Claude's own narration text over a generic
|
|
707
|
+
// placeholder — surfaces the same "what I'm doing / found" signal the CLI's
|
|
708
|
+
// own transcript view already shows. Falls back to a per-tool canned status
|
|
709
|
+
// (tv#291) when the assistant event carries a tool_use block but no
|
|
710
|
+
// narration text, and to 'Thinking...' when neither is present. Reads
|
|
711
|
+
// tool_use blocks from `event.message.content[]` rather than a top-level
|
|
712
|
+
// `event.type === 'tool_use'` check — see assistantCalledTool's comment
|
|
713
|
+
// above for why the latter never matches the real stream shape.
|
|
714
|
+
//
|
|
715
|
+
// `assistantText`, when passed, is the caller's already-extracted
|
|
716
|
+
// extractAssistantText(event) result (the stdout handler computes it a few
|
|
717
|
+
// lines above for the end-of-turn guard) — avoids re-parsing the same event
|
|
718
|
+
// twice per line. Falls back to extracting it internally so the function
|
|
719
|
+
// stays a self-contained, directly-testable unit.
|
|
720
|
+
export function resolveTypingStatus(event, assistantText) {
|
|
721
|
+
if (event?.type !== 'assistant' || !Array.isArray(event.message?.content)) {
|
|
722
|
+
return null;
|
|
723
|
+
}
|
|
724
|
+
// The caller clears the indicator when this tool sends a reply. Do not
|
|
725
|
+
// restore a status from that same event, even if it also contains narration.
|
|
726
|
+
if (assistantCalledTool(event, 'mcp__slack__send_message'))
|
|
727
|
+
return null;
|
|
728
|
+
const text = assistantText ?? extractAssistantText(event);
|
|
729
|
+
if (text) {
|
|
730
|
+
// Collapse newlines/whitespace runs so a multi-line narration doesn't
|
|
731
|
+
// land a literal `\n` in the one-line status surface.
|
|
732
|
+
const oneLine = text.replace(/\s+/g, ' ').trim();
|
|
733
|
+
return oneLine.length > TYPING_STATUS_MAX_LEN
|
|
734
|
+
? oneLine.slice(0, TYPING_STATUS_MAX_LEN - 1) + '…'
|
|
735
|
+
: oneLine;
|
|
736
|
+
}
|
|
737
|
+
for (const block of event.message.content) {
|
|
738
|
+
if (block?.type !== 'tool_use' || typeof block.name !== 'string')
|
|
739
|
+
continue;
|
|
740
|
+
const status = getStatusForTool(block.name);
|
|
741
|
+
if (status)
|
|
742
|
+
return status;
|
|
743
|
+
}
|
|
744
|
+
return 'Thinking...';
|
|
745
|
+
}
|
|
702
746
|
async function runClaudeCode(msg, sessionLog, cwd, brainPath, sessionId, isFirstMessage = true, lastMessageTs, onMessageSent, typingIndicator) {
|
|
703
747
|
const slackContext = msg.response_context.slack;
|
|
704
748
|
const isCronMessage = msg.source === 'cron';
|
|
@@ -1004,18 +1048,11 @@ async function runClaudeCode(msg, sessionLog, cwd, brainPath, sessionId, isFirst
|
|
|
1004
1048
|
scheduleProgressNotifications(progressNotificationMarks(minutes));
|
|
1005
1049
|
}
|
|
1006
1050
|
}
|
|
1007
|
-
// Auto-update typing indicator status based on tool usage
|
|
1051
|
+
// Auto-update typing indicator status based on tool usage (tv#369, tv#291)
|
|
1008
1052
|
if (typingIndicator) {
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
typingIndicator.updateStatus(status);
|
|
1013
|
-
}
|
|
1014
|
-
// When status is '' (e.g. set_status tool), don't override — Claude is managing it
|
|
1015
|
-
}
|
|
1016
|
-
else if (event.type === 'assistant') {
|
|
1017
|
-
// Reset to "Thinking..." when Claude is reasoning between tool calls
|
|
1018
|
-
typingIndicator.updateStatus('Thinking...');
|
|
1053
|
+
const status = resolveTypingStatus(event, assistantText);
|
|
1054
|
+
if (status) {
|
|
1055
|
+
typingIndicator.updateStatus(status);
|
|
1019
1056
|
}
|
|
1020
1057
|
}
|
|
1021
1058
|
}
|
package/dist/config.js
CHANGED
|
@@ -65,8 +65,12 @@ const configSchema = z.object({
|
|
|
65
65
|
POLLER_TZ: z.string().optional(),
|
|
66
66
|
MAINTENANCE_CRON: z.string().default('30 3 * * *'), // Daily at 03:30 in POLLER_TZ (or host TZ)
|
|
67
67
|
MAINTENANCE_ENABLED: z.string().default('true').transform((v) => v.toLowerCase() === 'true'),
|
|
68
|
-
// Auto-update settings
|
|
69
|
-
|
|
68
|
+
// Auto-update settings — defaults to 'off': most pollers run as pinned
|
|
69
|
+
// Railway container images (see Dockerfile), where an in-process
|
|
70
|
+
// `npm install -g` update can't survive the next redeploy anyway. Set
|
|
71
|
+
// explicitly (e.g. AUTO_UPDATE_POLICY=patch) for bare-metal/VM installs
|
|
72
|
+
// that actually benefit from self-updating.
|
|
73
|
+
AUTO_UPDATE_POLICY: z.enum(['patch', 'minor', 'off']).default('off'),
|
|
70
74
|
AUTO_UPDATE_SCHEDULE: z.string().default('03:00'), // HH:MM in UTC — daily check time
|
|
71
75
|
// Inbox watcher (teamvibe.ai#242): fs.watch()'s .inbox/ per tracked brain so a
|
|
72
76
|
// file dropped while no session is running (e.g. a background-task result)
|
package/dist/poller.js
CHANGED
|
@@ -219,6 +219,14 @@ export function describeFailureReason(killedByPoller, exitCode) {
|
|
|
219
219
|
// content an agent forgot to send via send_message) still delivers as
|
|
220
220
|
// before, so this can't silently drop a forgotten reply.
|
|
221
221
|
export const END_OF_TURN_NO_FURTHER_RESPONSE_SENTINEL = 'NO_FURTHER_RESPONSE_NEEDED';
|
|
222
|
+
// tv#358 (revised): the sentinel agents emit and the guard matches is
|
|
223
|
+
// unchanged — still the exact ALL_CAPS literal above, still an exact-match
|
|
224
|
+
// suppression. What changes is purely cosmetic: poller-brain#428's leak
|
|
225
|
+
// failure mode (a sentence written before the sentinel breaks the exact
|
|
226
|
+
// match, so the whole thing delivers as-is) showed the raw literal reads as
|
|
227
|
+
// jarring when it does surface. Swap it for a quieter display token
|
|
228
|
+
// wherever it appears in text that's actually being delivered.
|
|
229
|
+
const END_OF_TURN_SENTINEL_DISPLAY = '[silent]';
|
|
222
230
|
export function endOfTurnFallbackText(hasSlackContext, result) {
|
|
223
231
|
if (!hasSlackContext || !result.success || result.sawSendMessageCall) {
|
|
224
232
|
return undefined;
|
|
@@ -227,7 +235,7 @@ export function endOfTurnFallbackText(hasSlackContext, result) {
|
|
|
227
235
|
if (!text || text === END_OF_TURN_NO_FURTHER_RESPONSE_SENTINEL) {
|
|
228
236
|
return undefined;
|
|
229
237
|
}
|
|
230
|
-
return text;
|
|
238
|
+
return text.split(END_OF_TURN_NO_FURTHER_RESPONSE_SENTINEL).join(END_OF_TURN_SENTINEL_DISPLAY);
|
|
231
239
|
}
|
|
232
240
|
export async function runInboxLoop(threadId, kbPath, queueMessage, sessionLog) {
|
|
233
241
|
if (activeLoops.has(threadId)) {
|