@isomorph.ai/app-sdk 2.6.0 → 2.6.2
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/index.d.ts +2 -0
- package/dist/index.js +1 -1
- package/dist/integration-operations.d.ts +2 -2
- package/dist/integration-operations.js +3 -2
- package/dist/runtime.d.ts +5 -0
- package/dist/runtime.js +27 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -298,6 +298,8 @@ export interface GmailAttachmentInput {
|
|
|
298
298
|
export interface GmailMessageSendRequest {
|
|
299
299
|
operation: 'gmail.message.send';
|
|
300
300
|
resource: string;
|
|
301
|
+
/** `app` sends from the company mailbox IT connected (contracts 0.102.0); `user` (the default) sends as the signed-in person. */
|
|
302
|
+
mode?: SlackIdentityMode;
|
|
301
303
|
input: {
|
|
302
304
|
/** 1 to 50 email addresses per recipient list (at most 50 across to, cc and bcc). */
|
|
303
305
|
to: string[];
|
package/dist/index.js
CHANGED
|
@@ -835,7 +835,7 @@ export function createClient(options = {}) {
|
|
|
835
835
|
}
|
|
836
836
|
catch { /* not JSON: the status says what happened */ }
|
|
837
837
|
if (!response.ok || body.error) {
|
|
838
|
-
throw new IsomorphError(body.error?.category ?? 'INTERNAL', body.error?.message ?? `the public action answered ${response.status}`, body.requestId,
|
|
838
|
+
throw new IsomorphError(body.error?.category ?? 'INTERNAL', body.error?.message ?? `the public action answered ${response.status}`, body.requestId, body.error?.details, response.status);
|
|
839
839
|
}
|
|
840
840
|
return body.data;
|
|
841
841
|
},
|
|
@@ -65,9 +65,9 @@ export declare const IntegrationOperations: {
|
|
|
65
65
|
};
|
|
66
66
|
readonly "gmail.message.send": {
|
|
67
67
|
readonly kind: "saas";
|
|
68
|
-
readonly identities: readonly ["user"];
|
|
68
|
+
readonly identities: readonly ["user", "app"];
|
|
69
69
|
readonly readOnly: false;
|
|
70
|
-
readonly declaredMode:
|
|
70
|
+
readonly declaredMode: true;
|
|
71
71
|
readonly idempotencyKey: true;
|
|
72
72
|
};
|
|
73
73
|
readonly "gmail.attachment.read": {
|
|
@@ -90,10 +90,11 @@ export const IntegrationOperations = {
|
|
|
90
90
|
"gmail.message.send": {
|
|
91
91
|
"kind": "saas",
|
|
92
92
|
"identities": [
|
|
93
|
-
"user"
|
|
93
|
+
"user",
|
|
94
|
+
"app"
|
|
94
95
|
],
|
|
95
96
|
"readOnly": false,
|
|
96
|
-
"declaredMode":
|
|
97
|
+
"declaredMode": true,
|
|
97
98
|
"idempotencyKey": true
|
|
98
99
|
},
|
|
99
100
|
"gmail.attachment.read": {
|
package/dist/runtime.d.ts
CHANGED
|
@@ -70,6 +70,8 @@ export declare class Runs {
|
|
|
70
70
|
cancel(id: string, owner: string): Promise<JobRun | null>;
|
|
71
71
|
/** Rows whose lease ran out go back to the queue while attempts remain, else fail; how many there were. */
|
|
72
72
|
reclaim(now: Date, active?: ReadonlySet<string>): Promise<number>;
|
|
73
|
+
/** Deletes finished runs created before `before` (the queue index covers status and created_at); how many went. */
|
|
74
|
+
prune(before: Date): Promise<number>;
|
|
73
75
|
/** Claims the oldest queued run by a conditional update; null when the queue is empty or another worker won. */
|
|
74
76
|
claim(now: Date): Promise<JobRun | null>;
|
|
75
77
|
progress(id: string, percent: number, note?: string): Promise<void>;
|
|
@@ -120,6 +122,7 @@ export declare class Runtime {
|
|
|
120
122
|
private queueWorkers;
|
|
121
123
|
private queueWake;
|
|
122
124
|
private claimTail;
|
|
125
|
+
private pruneDue;
|
|
123
126
|
private readonly activeRuns;
|
|
124
127
|
private readonly runs;
|
|
125
128
|
private session;
|
|
@@ -193,6 +196,8 @@ export declare class Runtime {
|
|
|
193
196
|
* when the queue is empty or both execution slots are occupied.
|
|
194
197
|
*/
|
|
195
198
|
work(): Promise<boolean>;
|
|
199
|
+
/** A failed prune is logged and tried again next day; it never stops the pass that carries it. */
|
|
200
|
+
private pruneRuns;
|
|
196
201
|
private serve;
|
|
197
202
|
private transaction;
|
|
198
203
|
private loadFailures;
|
package/dist/runtime.js
CHANGED
|
@@ -88,6 +88,12 @@ const MAX_BODY_BYTES = 1 << 20;
|
|
|
88
88
|
const LEASE_MS = 5 * 60_000;
|
|
89
89
|
const QUEUE_WORKERS = 2;
|
|
90
90
|
const QUEUE_POLL_MS = 2_000;
|
|
91
|
+
// A finished run's row (input, result, error) is kept this long, then the
|
|
92
|
+
// queue loop deletes it: once per process start and once a day after that,
|
|
93
|
+
// never on every pass. Nothing else ever removed a row (the outbox backlog
|
|
94
|
+
// of 2026-10-26 was the same shape).
|
|
95
|
+
const RUN_RETENTION_MS = 30 * 24 * 60 * 60_000;
|
|
96
|
+
const PRUNE_EVERY_MS = 24 * 60 * 60_000;
|
|
91
97
|
const AI_RETRY_MS = 30_000;
|
|
92
98
|
const TOKEN_REFRESH_MS = 60_000;
|
|
93
99
|
// A gateway session lasts an hour. An action runs at most 300 s, so a request
|
|
@@ -280,6 +286,11 @@ export class Runs {
|
|
|
280
286
|
}
|
|
281
287
|
return interrupted.length;
|
|
282
288
|
}
|
|
289
|
+
/** Deletes finished runs created before `before` (the queue index covers status and created_at); how many went. */
|
|
290
|
+
async prune(before) {
|
|
291
|
+
const { data } = await this.table().delete().in('status', ['succeeded', 'failed', 'cancelled']).lt('created_at', before.toISOString()).select('id');
|
|
292
|
+
return data.length;
|
|
293
|
+
}
|
|
283
294
|
/** Claims the oldest queued run by a conditional update; null when the queue is empty or another worker won. */
|
|
284
295
|
async claim(now) {
|
|
285
296
|
while (true) {
|
|
@@ -332,6 +343,7 @@ export class Runtime {
|
|
|
332
343
|
queueWorkers = 0;
|
|
333
344
|
queueWake = 0;
|
|
334
345
|
claimTail = Promise.resolve();
|
|
346
|
+
pruneDue = 0;
|
|
335
347
|
activeRuns = new Set();
|
|
336
348
|
runs;
|
|
337
349
|
// Built on first use: the AWS signing modules load only on a serverless
|
|
@@ -671,6 +683,10 @@ export class Runtime {
|
|
|
671
683
|
if (this.activeRuns.size >= QUEUE_WORKERS)
|
|
672
684
|
return false;
|
|
673
685
|
const now = new Date();
|
|
686
|
+
if (now.getTime() >= this.pruneDue) {
|
|
687
|
+
this.pruneDue = now.getTime() + PRUNE_EVERY_MS;
|
|
688
|
+
await this.pruneRuns(now);
|
|
689
|
+
}
|
|
674
690
|
reclaimed = await this.runs.reclaim(now, this.activeRuns);
|
|
675
691
|
run = await this.runs.claim(now);
|
|
676
692
|
if (run)
|
|
@@ -711,6 +727,17 @@ export class Runtime {
|
|
|
711
727
|
this.activeRuns.delete(run.id);
|
|
712
728
|
}
|
|
713
729
|
}
|
|
730
|
+
/** A failed prune is logged and tried again next day; it never stops the pass that carries it. */
|
|
731
|
+
async pruneRuns(now) {
|
|
732
|
+
try {
|
|
733
|
+
const pruned = await this.runs.prune(new Date(now.getTime() - RUN_RETENTION_MS));
|
|
734
|
+
if (pruned > 0)
|
|
735
|
+
this.log(`pruned ${pruned} finished run(s) older than 30 days`);
|
|
736
|
+
}
|
|
737
|
+
catch (error) {
|
|
738
|
+
this.log(`pruning finished runs failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
714
741
|
async serve(request, response) {
|
|
715
742
|
const requestId = header(request, 'x-request-id') || undefined;
|
|
716
743
|
const name = decodeURIComponent((request.url ?? '/').split('?')[0].replace(/^\/+/, ''));
|