crewly 1.20.301 → 1.20.302
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/config/skills/agent/core/receive-file/SKILL.md +4 -0
- package/config/skills/agent/core/request-file/SKILL.md +74 -0
- package/config/skills/agent/core/request-file/execute.sh +89 -0
- package/config/skills/agent/core/request-file/execute.test.sh +77 -0
- package/config/skills/agent/core/slack-file/SKILL.md +1 -1
- package/dist/backend/backend/src/constants.d.ts +13 -4
- package/dist/backend/backend/src/constants.d.ts.map +1 -1
- package/dist/backend/backend/src/constants.js +18 -6
- package/dist/backend/backend/src/constants.js.map +1 -1
- package/dist/backend/backend/src/controllers/apps/apps.controller.d.ts +9 -0
- package/dist/backend/backend/src/controllers/apps/apps.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/apps/apps.controller.js +21 -0
- package/dist/backend/backend/src/controllers/apps/apps.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/apps/apps.routes.d.ts +2 -0
- package/dist/backend/backend/src/controllers/apps/apps.routes.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/apps/apps.routes.js +7 -1
- package/dist/backend/backend/src/controllers/apps/apps.routes.js.map +1 -1
- package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.d.ts.map +1 -1
- package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.js +1 -0
- package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.js.map +1 -1
- package/dist/backend/backend/src/services/apps/apps.wiring.d.ts.map +1 -1
- package/dist/backend/backend/src/services/apps/apps.wiring.js +14 -0
- package/dist/backend/backend/src/services/apps/apps.wiring.js.map +1 -1
- package/dist/backend/backend/src/services/apps/file-request-watch.service.d.ts +88 -0
- package/dist/backend/backend/src/services/apps/file-request-watch.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/apps/file-request-watch.service.js +170 -0
- package/dist/backend/backend/src/services/apps/file-request-watch.service.js.map +1 -0
- package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js +51 -33
- package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js.map +1 -1
- package/dist/backend/backend/src/types/slack.types.d.ts +8 -0
- package/dist/backend/backend/src/types/slack.types.d.ts.map +1 -1
- package/dist/backend/backend/src/types/slack.types.js.map +1 -1
- package/dist/backend/build-info.json +2 -2
- package/dist/cli/backend/src/constants.d.ts +13 -4
- package/dist/cli/backend/src/constants.d.ts.map +1 -1
- package/dist/cli/backend/src/constants.js +18 -6
- package/dist/cli/backend/src/constants.js.map +1 -1
- package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.d.ts.map +1 -1
- package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.js +1 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.js.map +1 -1
- package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.d.ts.map +1 -1
- package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.js +51 -33
- package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.js.map +1 -1
- package/dist/cli/backend/src/services/worktree/low-disk-guard.d.ts +109 -0
- package/dist/cli/backend/src/services/worktree/low-disk-guard.d.ts.map +1 -0
- package/dist/cli/backend/src/services/worktree/low-disk-guard.js +148 -0
- package/dist/cli/backend/src/services/worktree/low-disk-guard.js.map +1 -0
- package/dist/cli/backend/src/services/worktree/worktree-janitor.git.d.ts +224 -0
- package/dist/cli/backend/src/services/worktree/worktree-janitor.git.d.ts.map +1 -0
- package/dist/cli/backend/src/services/worktree/worktree-janitor.git.js +449 -0
- package/dist/cli/backend/src/services/worktree/worktree-janitor.git.js.map +1 -0
- package/dist/cli/backend/src/types/slack.types.d.ts +8 -0
- package/dist/cli/backend/src/types/slack.types.d.ts.map +1 -1
- package/dist/cli/backend/src/types/slack.types.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FileRequestWatchService — tells an agent when the owner has uploaded the
|
|
3
|
+
* file it asked for with the `request-file` skill.
|
|
4
|
+
*
|
|
5
|
+
* The owner uploads from the Crewly portal (a signed-in link, chunked); Cloud
|
|
6
|
+
* keeps the file as a handoff addressed to this machine's agent. This service
|
|
7
|
+
* reads the instance's inbox (`GET /upload-requests/inbox`) every
|
|
8
|
+
* FILE_REQUEST_POLL_MS, hands each new file to the agent that asked (starting
|
|
9
|
+
* it when it is down; the orchestrator when the agent is gone from this
|
|
10
|
+
* machine), and then marks the file told so it is delivered once. A failing
|
|
11
|
+
* poll backs off (x2 up to FILE_REQUEST_MAX_BACKOFF_MS); nothing is lost,
|
|
12
|
+
* because Cloud keeps un-told files in the inbox until they are marked.
|
|
13
|
+
*
|
|
14
|
+
* The message names the file and the `receive-file` command; the file
|
|
15
|
+
* name is the owner's text, so it is quoted and length-capped.
|
|
16
|
+
*
|
|
17
|
+
* @module services/apps/file-request-watch.service
|
|
18
|
+
*/
|
|
19
|
+
import { CREWLY_APPS_CONSTANTS } from '../../constants.js';
|
|
20
|
+
import { LoggerService } from '../core/logger.service.js';
|
|
21
|
+
import { AppsCloudError } from './apps-cloud.client.js';
|
|
22
|
+
const C = CREWLY_APPS_CONSTANTS;
|
|
23
|
+
/**
|
|
24
|
+
* Human size: 23273672 -> "22.2 MB".
|
|
25
|
+
*
|
|
26
|
+
* @param bytes - Size
|
|
27
|
+
* @returns Text
|
|
28
|
+
*/
|
|
29
|
+
export function formatSize(bytes) {
|
|
30
|
+
if (bytes < 1024 * 1024)
|
|
31
|
+
return `${Math.max(1, Math.round(bytes / 1024))} KB`;
|
|
32
|
+
if (bytes < 1024 ** 3)
|
|
33
|
+
return `${(bytes / 1024 ** 2).toFixed(1)} MB`;
|
|
34
|
+
return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The message an agent gets when the owner's upload arrived.
|
|
38
|
+
*
|
|
39
|
+
* @param items - Files of one request (same agent)
|
|
40
|
+
* @param receiveCommand - Command that receives one handoff
|
|
41
|
+
* @returns Message text
|
|
42
|
+
*/
|
|
43
|
+
export function buildFileUploadedMessage(items, receiveCommand) {
|
|
44
|
+
const first = items[0];
|
|
45
|
+
const safeName = (n) => JSON.stringify(n.replace(/[\u0000-\u001f\u007f]/g, ' ').slice(0, 120));
|
|
46
|
+
const lines = items.map((i) => `- ${safeName(i.fileName)} (${formatSize(i.sizeBytes)}): ${receiveCommand(i.handoffId)}`);
|
|
47
|
+
return [
|
|
48
|
+
`[File from the owner] The owner uploaded ${items.length === 1 ? 'the file' : `${items.length} files`} you asked for with request-file` +
|
|
49
|
+
` (your request: ${JSON.stringify(first.purpose.slice(0, 200))}).`,
|
|
50
|
+
'Receive each one with the receive-file skill, then carry on with what you were doing. Tell the owner in your own words that you have it.',
|
|
51
|
+
...lines,
|
|
52
|
+
].join('\n');
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Service — see module docs.
|
|
56
|
+
*/
|
|
57
|
+
export class FileRequestWatchService {
|
|
58
|
+
deps;
|
|
59
|
+
timer = null;
|
|
60
|
+
stopped = true;
|
|
61
|
+
failures = 0;
|
|
62
|
+
logger;
|
|
63
|
+
/** Handoff ids delivered but not yet marked told (a mark that failed is retried, not re-sent). */
|
|
64
|
+
delivered = new Set();
|
|
65
|
+
constructor(deps) {
|
|
66
|
+
this.deps = deps;
|
|
67
|
+
this.logger = LoggerService.getInstance().createComponentLogger('FileRequestWatch');
|
|
68
|
+
}
|
|
69
|
+
/** Start polling (idempotent). */
|
|
70
|
+
start() {
|
|
71
|
+
if (!this.stopped)
|
|
72
|
+
return;
|
|
73
|
+
this.stopped = false;
|
|
74
|
+
this.schedule(5_000);
|
|
75
|
+
}
|
|
76
|
+
/** Stop polling. */
|
|
77
|
+
stop() {
|
|
78
|
+
this.stopped = true;
|
|
79
|
+
if (this.timer)
|
|
80
|
+
clearTimeout(this.timer);
|
|
81
|
+
this.timer = null;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Read the inbox once and deliver what is new.
|
|
85
|
+
*
|
|
86
|
+
* @returns How many files were delivered this pass
|
|
87
|
+
*/
|
|
88
|
+
async pollOnce() {
|
|
89
|
+
const data = await this.deps.client.request('GET', '/upload-requests/inbox', {
|
|
90
|
+
timeoutMs: C.FILE_REQUEST_TIMEOUT_MS,
|
|
91
|
+
});
|
|
92
|
+
const items = Array.isArray(data?.items) ? data.items : [];
|
|
93
|
+
// One message per (request, agent).
|
|
94
|
+
const groups = new Map();
|
|
95
|
+
for (const i of items) {
|
|
96
|
+
if (!i || typeof i.handoffId !== 'string' || typeof i.requestId !== 'string' || typeof i.agentSession !== 'string')
|
|
97
|
+
continue;
|
|
98
|
+
const key = `${i.requestId}\u0000${i.agentSession}`;
|
|
99
|
+
groups.set(key, [...(groups.get(key) ?? []), i]);
|
|
100
|
+
}
|
|
101
|
+
let count = 0;
|
|
102
|
+
for (const group of groups.values()) {
|
|
103
|
+
const fresh = group.filter((i) => !this.delivered.has(i.handoffId));
|
|
104
|
+
if (fresh.length > 0 && (await this.deliver(fresh))) {
|
|
105
|
+
for (const i of fresh)
|
|
106
|
+
this.delivered.add(i.handoffId);
|
|
107
|
+
count += fresh.length;
|
|
108
|
+
}
|
|
109
|
+
else if (fresh.length > 0) {
|
|
110
|
+
continue; // not delivered: stays in the inbox, retried next pass
|
|
111
|
+
}
|
|
112
|
+
await this.markTold(group);
|
|
113
|
+
}
|
|
114
|
+
return count;
|
|
115
|
+
}
|
|
116
|
+
async deliver(items) {
|
|
117
|
+
const { agentSession } = items[0];
|
|
118
|
+
const command = this.deps.receiveCommand ?? ((id) => `bash <skills>/core/receive-file/execute.sh --id ${id}`);
|
|
119
|
+
const text = buildFileUploadedMessage(items, command);
|
|
120
|
+
const local = await this.deps.isLocalAgent(agentSession).catch(() => false);
|
|
121
|
+
if (!local) {
|
|
122
|
+
// The agent is gone from this machine (renamed or removed): the orchestrator routes it.
|
|
123
|
+
return this.deps.sendToOrchestrator(`The agent "${agentSession}" is not on this machine any more.\n${text}`);
|
|
124
|
+
}
|
|
125
|
+
const send = () => this.deps.sendToAgent(agentSession, text, true);
|
|
126
|
+
const ref = `file-request:${items[0].handoffId}`;
|
|
127
|
+
return this.deps.asOwnerMessage ? this.deps.asOwnerMessage(agentSession, text, ref, send) : send();
|
|
128
|
+
}
|
|
129
|
+
async markTold(group) {
|
|
130
|
+
const ids = group.map((i) => i.handoffId).filter((id) => this.delivered.has(id));
|
|
131
|
+
if (ids.length === 0)
|
|
132
|
+
return;
|
|
133
|
+
try {
|
|
134
|
+
await this.deps.client.request('POST', `/upload-requests/${group[0].requestId}/notified`, {
|
|
135
|
+
body: { handoffIds: ids },
|
|
136
|
+
timeoutMs: C.FILE_REQUEST_TIMEOUT_MS,
|
|
137
|
+
});
|
|
138
|
+
for (const id of ids)
|
|
139
|
+
this.delivered.delete(id);
|
|
140
|
+
}
|
|
141
|
+
catch (err) {
|
|
142
|
+
this.logger.warn('Could not mark uploaded files as told (retried next pass)', { error: err instanceof Error ? err.message : String(err) });
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
schedule(ms) {
|
|
146
|
+
if (this.stopped)
|
|
147
|
+
return;
|
|
148
|
+
this.timer = setTimeout(() => void this.tick(), ms);
|
|
149
|
+
this.timer.unref?.();
|
|
150
|
+
}
|
|
151
|
+
async tick() {
|
|
152
|
+
const pollMs = this.deps.pollMs ?? C.FILE_REQUEST_POLL_MS;
|
|
153
|
+
const maxBackoff = this.deps.maxBackoffMs ?? C.FILE_REQUEST_MAX_BACKOFF_MS;
|
|
154
|
+
try {
|
|
155
|
+
const n = await this.pollOnce();
|
|
156
|
+
if (n > 0)
|
|
157
|
+
this.logger.info('Owner upload delivered to its agent', { files: n });
|
|
158
|
+
this.failures = 0;
|
|
159
|
+
}
|
|
160
|
+
catch (err) {
|
|
161
|
+
// Not signed in to Cloud / offline: quiet, backs off.
|
|
162
|
+
const quiet = err instanceof AppsCloudError && (err.status === 409 || err.status === 401 || err.status === 503);
|
|
163
|
+
this.failures++;
|
|
164
|
+
if (!quiet && this.failures <= 3)
|
|
165
|
+
this.logger.warn('Upload inbox poll failed', { error: err instanceof Error ? err.message : String(err) });
|
|
166
|
+
}
|
|
167
|
+
this.schedule(Math.min(pollMs * 2 ** Math.min(this.failures, 10), maxBackoff));
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=file-request-watch.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-request-watch.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/apps/file-request-watch.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slack-orchestrator-bridge.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/slack/slack-orchestrator-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAStC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAWrE,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,wBAAwB,EAOxB,kBAAkB,EAEnB,MAAM,4BAA4B,CAAC;AAGpC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uCAAuC,CAAC;AACjF,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"slack-orchestrator-bridge.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/slack/slack-orchestrator-bridge.ts"],"names":[],"mappings":"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"}
|
|
@@ -26,7 +26,8 @@ import { ContentApprovalService } from '../onboarding/content-approval.service.j
|
|
|
26
26
|
import { getSlackImageService } from './slack-image.service.js';
|
|
27
27
|
import { getOwnerMessageWatchdog } from '../messaging/owner-message-watchdog.service.js';
|
|
28
28
|
import { isOwnerAuthored } from './slack-auto-working.service.js';
|
|
29
|
-
import { ORCHESTRATOR_SESSION_NAME, MESSAGE_QUEUE_CONSTANTS, SLACK_IMAGE_CONSTANTS, SLACK_FILE_DOWNLOAD_CONSTANTS, SLACK_FILE_FETCH_CONSTANTS, SLACK_BRIDGE_CONSTANTS, AUDITOR_SCHEDULER_CONSTANTS, THREAD_STATUS_CONSTANTS, OWNER_EVIDENCE_METADATA } from '../../constants.js';
|
|
29
|
+
import { ORCHESTRATOR_SESSION_NAME, MESSAGE_QUEUE_CONSTANTS, SLACK_IMAGE_CONSTANTS, SLACK_FILE_DOWNLOAD_CONSTANTS, SLACK_FILE_FETCH_CONSTANTS, SLACK_BRIDGE_CONSTANTS, AUDITOR_SCHEDULER_CONSTANTS, THREAD_STATUS_CONSTANTS, OWNER_EVIDENCE_METADATA, WORKTREE_JANITOR_CONSTANTS } from '../../constants.js';
|
|
30
|
+
import { readFreeBytes } from '../worktree/low-disk-guard.js';
|
|
30
31
|
import { LoggerService } from '../core/logger.service.js';
|
|
31
32
|
import { CROSS_MACHINE_PREFIX } from '../../types/cross-machine.types.js';
|
|
32
33
|
import { getCrossMachineMessageService } from './cross-machine-message.service.js';
|
|
@@ -1440,21 +1441,16 @@ Just type naturally to chat with the orchestrator!`;
|
|
|
1440
1441
|
if (downloadedImages.length > 0) {
|
|
1441
1442
|
message.images = downloadedImages;
|
|
1442
1443
|
}
|
|
1443
|
-
//
|
|
1444
|
+
// Rejections are told to the agent (enrichTextWithFiles), not posted into the
|
|
1445
|
+
// owner's thread: no system warning lines there.
|
|
1444
1446
|
if (rejectionMessages.length > 0) {
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
threadTs: message.threadTs || message.ts,
|
|
1453
|
-
});
|
|
1454
|
-
}
|
|
1455
|
-
catch (err) {
|
|
1456
|
-
this.logger.warn('Failed to send rejection warning to Slack', { error: err instanceof Error ? err.message : String(err) });
|
|
1457
|
-
}
|
|
1447
|
+
message.fileFailures = [
|
|
1448
|
+
...(message.fileFailures ?? []),
|
|
1449
|
+
...rejectionMessages.map((m) => {
|
|
1450
|
+
const i = m.indexOf(': ');
|
|
1451
|
+
return { name: m.slice(0, i), reason: m.slice(i + 2) };
|
|
1452
|
+
}),
|
|
1453
|
+
];
|
|
1458
1454
|
}
|
|
1459
1455
|
}
|
|
1460
1456
|
/**
|
|
@@ -1486,6 +1482,12 @@ Just type naturally to chat with the orchestrator!`;
|
|
|
1486
1482
|
const maxMB = Math.round(SLACK_FILE_DOWNLOAD_CONSTANTS.MAX_FILE_SIZE / (1024 * 1024));
|
|
1487
1483
|
throw new Error(`File too large: ${file.size} bytes (max ${maxMB} MB)`);
|
|
1488
1484
|
}
|
|
1485
|
+
// Never fill the disk: keep the low-disk threshold free after the download.
|
|
1486
|
+
const free = await readFreeBytes(tempDir);
|
|
1487
|
+
if (free !== null && free - file.size < WORKTREE_JANITOR_CONSTANTS.LOW_DISK_BYTES) {
|
|
1488
|
+
throw new Error(`Not enough disk space: ${file.size} bytes needed, ${free} bytes free ` +
|
|
1489
|
+
`(at least ${WORKTREE_JANITOR_CONSTANTS.LOW_DISK_BYTES} bytes must stay free)`);
|
|
1490
|
+
}
|
|
1489
1491
|
const safeName = file.name.replace(/[^a-zA-Z0-9._-]/g, '_');
|
|
1490
1492
|
const localPath = path.join(tempDir, `${file.id}-${safeName}`);
|
|
1491
1493
|
const downloadUrl = file.url_private_download || file.url_private;
|
|
@@ -1493,7 +1495,13 @@ Just type naturally to chat with the orchestrator!`;
|
|
|
1493
1495
|
const maxRedirects = SLACK_FILE_DOWNLOAD_CONSTANTS.MAX_DOWNLOAD_REDIRECTS;
|
|
1494
1496
|
const timeoutMs = SLACK_FILE_DOWNLOAD_CONSTANTS.DOWNLOAD_TIMEOUT_MS;
|
|
1495
1497
|
const controller = new AbortController();
|
|
1496
|
-
|
|
1498
|
+
// The timeout is an idle timeout: reset on every received chunk so a large
|
|
1499
|
+
// file can take as long as it needs while a stalled connection still aborts.
|
|
1500
|
+
let timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
1501
|
+
const resetTimeout = () => {
|
|
1502
|
+
clearTimeout(timeoutId);
|
|
1503
|
+
timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
1504
|
+
};
|
|
1497
1505
|
let currentUrl = downloadUrl;
|
|
1498
1506
|
let response = null;
|
|
1499
1507
|
try {
|
|
@@ -1527,7 +1535,8 @@ Just type naturally to chat with the orchestrator!`;
|
|
|
1527
1535
|
}
|
|
1528
1536
|
const fileStream = createWriteStream(localPath);
|
|
1529
1537
|
const nodeStream = Readable.fromWeb(response.body);
|
|
1530
|
-
|
|
1538
|
+
nodeStream.on('data', resetTimeout);
|
|
1539
|
+
await pipeline(nodeStream, fileStream, { signal: controller.signal });
|
|
1531
1540
|
}
|
|
1532
1541
|
catch (downloadErr) {
|
|
1533
1542
|
// Clean up partial file on failure
|
|
@@ -1564,7 +1573,7 @@ Just type naturally to chat with the orchestrator!`;
|
|
|
1564
1573
|
const fileName = batch[j].name;
|
|
1565
1574
|
const reason = result.reason instanceof Error ? result.reason.message : String(result.reason);
|
|
1566
1575
|
this.logger.warn('Failed to download file', { fileName, reason });
|
|
1567
|
-
if (reason.startsWith('File too large:')) {
|
|
1576
|
+
if (reason.startsWith('File too large:') || reason.startsWith('Not enough disk space:')) {
|
|
1568
1577
|
rejectionMessages.push(`${fileName}: ${reason}`);
|
|
1569
1578
|
}
|
|
1570
1579
|
}
|
|
@@ -1573,19 +1582,15 @@ Just type naturally to chat with the orchestrator!`;
|
|
|
1573
1582
|
if (downloadedFiles.length > 0) {
|
|
1574
1583
|
message.attachments = downloadedFiles;
|
|
1575
1584
|
}
|
|
1585
|
+
// Told to the agent (enrichTextWithFiles), never posted into the owner's thread.
|
|
1576
1586
|
if (rejectionMessages.length > 0) {
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
});
|
|
1585
|
-
}
|
|
1586
|
-
catch (err) {
|
|
1587
|
-
this.logger.warn('Failed to send file rejection warning to Slack', { error: err instanceof Error ? err.message : String(err) });
|
|
1588
|
-
}
|
|
1587
|
+
message.fileFailures = [
|
|
1588
|
+
...(message.fileFailures ?? []),
|
|
1589
|
+
...rejectionMessages.map((m) => {
|
|
1590
|
+
const i = m.indexOf(': ');
|
|
1591
|
+
return { name: m.slice(0, i), reason: m.slice(i + 2) };
|
|
1592
|
+
}),
|
|
1593
|
+
];
|
|
1589
1594
|
}
|
|
1590
1595
|
}
|
|
1591
1596
|
/**
|
|
@@ -1702,18 +1707,31 @@ Just type naturally to chat with the orchestrator!`;
|
|
|
1702
1707
|
// saw "have a look at this" with nothing to look at and answered about
|
|
1703
1708
|
// the previous topic instead of saying it could not see the attachment
|
|
1704
1709
|
// (2026-09-21, #daily-info). Name what is missing.
|
|
1710
|
+
const explained = new Set();
|
|
1711
|
+
for (const f of message.fileFailures ?? []) {
|
|
1712
|
+
if (explained.has(f.name))
|
|
1713
|
+
continue;
|
|
1714
|
+
explained.add(f.name);
|
|
1715
|
+
const tooBig = f.reason.startsWith('File too large:') || f.reason.startsWith('Not enough disk space:');
|
|
1716
|
+
text += `\n[Slack: the attachment ${f.name} could not be downloaded (${f.reason}).` +
|
|
1717
|
+
(tooBig
|
|
1718
|
+
? ' Do not ask the owner to compress it or use Drive. Use the `request-file` skill to send the owner an upload link, and explain it in your own words.'
|
|
1719
|
+
: '') +
|
|
1720
|
+
']';
|
|
1721
|
+
}
|
|
1705
1722
|
const delivered = (message.images?.length ?? 0) + (message.attachments?.length ?? 0);
|
|
1706
1723
|
const attached = message.files?.length ?? 0;
|
|
1707
|
-
if (attached > delivered) {
|
|
1724
|
+
if (attached > delivered + explained.size) {
|
|
1708
1725
|
const undelivered = (message.files ?? [])
|
|
1709
1726
|
.filter((f) => {
|
|
1710
1727
|
const name = f.name ?? '';
|
|
1711
|
-
return (!
|
|
1728
|
+
return (!explained.has(name) &&
|
|
1729
|
+
!(message.images ?? []).some((i) => i.name === name) &&
|
|
1712
1730
|
!(message.attachments ?? []).some((a) => a.name === name));
|
|
1713
1731
|
})
|
|
1714
1732
|
.map((f) => f.name || 'unnamed')
|
|
1715
1733
|
.join(', ');
|
|
1716
|
-
text += `\n[Slack: ${attached - delivered} attachment(s) could not be read${undelivered ? ` — ${undelivered}` : ''}. Say so rather than guessing what they contained; the owner may need to invite your Slack app to this channel.]`;
|
|
1734
|
+
text += `\n[Slack: ${attached - delivered - explained.size} attachment(s) could not be read${undelivered ? ` — ${undelivered}` : ''}. Say so rather than guessing what they contained; the owner may need to invite your Slack app to this channel.]`;
|
|
1717
1735
|
}
|
|
1718
1736
|
return text;
|
|
1719
1737
|
}
|