@astralform/js 4.8.0 → 4.9.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/index.cjs +88 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +34 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.js +88 -26
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -297,6 +297,7 @@ interface WireMessageStop extends WireEnvelope {
|
|
|
297
297
|
input_tokens?: number;
|
|
298
298
|
output_tokens?: number;
|
|
299
299
|
cached_tokens?: number;
|
|
300
|
+
cache_creation_tokens?: number;
|
|
300
301
|
};
|
|
301
302
|
ttfb_ms?: number | null;
|
|
302
303
|
total_ms: number;
|
|
@@ -344,6 +345,8 @@ interface TurnUsage {
|
|
|
344
345
|
inputTokens: number;
|
|
345
346
|
outputTokens: number;
|
|
346
347
|
cachedTokens: number;
|
|
348
|
+
/** Tokens written to the model's prompt cache (wire: `cache_creation_tokens`). */
|
|
349
|
+
cacheCreationTokens: number;
|
|
347
350
|
}
|
|
348
351
|
type BlockDeltaPayload = {
|
|
349
352
|
channel: "text";
|
|
@@ -629,6 +632,8 @@ interface TeamAgentSummary {
|
|
|
629
632
|
teamId: string;
|
|
630
633
|
createdAt: string;
|
|
631
634
|
updatedAt: string;
|
|
635
|
+
/** Agent avatar URL, when the team has set one (wire: `avatar_url`). */
|
|
636
|
+
avatarUrl?: string | null;
|
|
632
637
|
}
|
|
633
638
|
interface SkillInfo {
|
|
634
639
|
name: string;
|
|
@@ -700,6 +705,7 @@ interface ChatStreamRequest {
|
|
|
700
705
|
agent_name?: string;
|
|
701
706
|
plan_mode?: boolean;
|
|
702
707
|
image_mode?: boolean;
|
|
708
|
+
video_mode?: boolean;
|
|
703
709
|
/**
|
|
704
710
|
* Start a durable long-horizon goal for this run (goal mode). The backend mints
|
|
705
711
|
* an agent_goal from this text and drives the run under a budget until the
|
|
@@ -805,6 +811,22 @@ interface SendOptions$1 extends ModelChoiceOptions {
|
|
|
805
811
|
* The `image_mode` field itself is accepted from Astralform ≥ 0.59.0.
|
|
806
812
|
*/
|
|
807
813
|
imageMode?: boolean;
|
|
814
|
+
/**
|
|
815
|
+
* Put this turn in video mode, attaching the video-generation tool.
|
|
816
|
+
*
|
|
817
|
+
* Off by default and per-message, for a sharper reason than images: a clip
|
|
818
|
+
* occupies one shared GPU for minutes, during which image generation on the
|
|
819
|
+
* same host cannot run at all. The tool is not attached unless this is set.
|
|
820
|
+
*
|
|
821
|
+
* Mutually exclusive with `imageMode` at the composer level — which is why a
|
|
822
|
+
* video turn also gets the image tool server-side: the user cannot select
|
|
823
|
+
* both, so the agent must be able to produce its own first frame.
|
|
824
|
+
*
|
|
825
|
+
* `generate_video` animates an EXISTING image; it cannot start from text, and
|
|
826
|
+
* the clip is silent. Gate the affordance on `AgentStatus.capabilities`
|
|
827
|
+
* (`video`), the same way image mode does.
|
|
828
|
+
*/
|
|
829
|
+
videoMode?: boolean;
|
|
808
830
|
/**
|
|
809
831
|
* Start a durable long-horizon goal for this run (goal mode). The text becomes
|
|
810
832
|
* the goal's objective; the backend keeps the agent working under a budget until
|
|
@@ -1158,6 +1180,11 @@ declare class ChatSession {
|
|
|
1158
1180
|
* Consume a single SSE stream to exhaustion. Returns whether a terminal
|
|
1159
1181
|
* event (``message_stop`` / ``error``) was seen, so the caller can decide
|
|
1160
1182
|
* whether an ended stream means "turn done" vs "dropped, reconnect".
|
|
1183
|
+
*
|
|
1184
|
+
* ``onStall`` aborts the per-attempt connection: if no event arrives within
|
|
1185
|
+
* SSE_STALL_TIMEOUT_MS (backend keepalives land every 15s), the stream is a
|
|
1186
|
+
* zombie — ``reader.read()`` will never settle — so we kill the fetch and
|
|
1187
|
+
* throw a ConnectionError, feeding the caller's reconnect-from-lastSeq loop.
|
|
1161
1188
|
*/
|
|
1162
1189
|
private pumpStream;
|
|
1163
1190
|
/** Sleep for ``ms``, resolving early if the turn is aborted mid-backoff. */
|
|
@@ -1334,6 +1361,13 @@ interface SendOptions extends ModelChoiceOptions {
|
|
|
1334
1361
|
* provider. Gate the affordance on `AgentStatus.capabilities`.
|
|
1335
1362
|
*/
|
|
1336
1363
|
imageMode?: boolean;
|
|
1364
|
+
/**
|
|
1365
|
+
* Attach the video-generation tool to this turn. Mutually exclusive with
|
|
1366
|
+
* `imageMode` at the composer level. See `SendOptions.videoMode` in types.ts
|
|
1367
|
+
* for the full rule — a clip animates an existing image, is silent, and holds
|
|
1368
|
+
* one shared GPU for minutes, so it is per-message and off by default.
|
|
1369
|
+
*/
|
|
1370
|
+
videoMode?: boolean;
|
|
1337
1371
|
/**
|
|
1338
1372
|
* Start a durable long-horizon goal for this run (goal mode) — the text is the
|
|
1339
1373
|
* goal objective the backend drives to completion. Omit for a normal turn.
|
package/dist/index.d.ts
CHANGED
|
@@ -297,6 +297,7 @@ interface WireMessageStop extends WireEnvelope {
|
|
|
297
297
|
input_tokens?: number;
|
|
298
298
|
output_tokens?: number;
|
|
299
299
|
cached_tokens?: number;
|
|
300
|
+
cache_creation_tokens?: number;
|
|
300
301
|
};
|
|
301
302
|
ttfb_ms?: number | null;
|
|
302
303
|
total_ms: number;
|
|
@@ -344,6 +345,8 @@ interface TurnUsage {
|
|
|
344
345
|
inputTokens: number;
|
|
345
346
|
outputTokens: number;
|
|
346
347
|
cachedTokens: number;
|
|
348
|
+
/** Tokens written to the model's prompt cache (wire: `cache_creation_tokens`). */
|
|
349
|
+
cacheCreationTokens: number;
|
|
347
350
|
}
|
|
348
351
|
type BlockDeltaPayload = {
|
|
349
352
|
channel: "text";
|
|
@@ -629,6 +632,8 @@ interface TeamAgentSummary {
|
|
|
629
632
|
teamId: string;
|
|
630
633
|
createdAt: string;
|
|
631
634
|
updatedAt: string;
|
|
635
|
+
/** Agent avatar URL, when the team has set one (wire: `avatar_url`). */
|
|
636
|
+
avatarUrl?: string | null;
|
|
632
637
|
}
|
|
633
638
|
interface SkillInfo {
|
|
634
639
|
name: string;
|
|
@@ -700,6 +705,7 @@ interface ChatStreamRequest {
|
|
|
700
705
|
agent_name?: string;
|
|
701
706
|
plan_mode?: boolean;
|
|
702
707
|
image_mode?: boolean;
|
|
708
|
+
video_mode?: boolean;
|
|
703
709
|
/**
|
|
704
710
|
* Start a durable long-horizon goal for this run (goal mode). The backend mints
|
|
705
711
|
* an agent_goal from this text and drives the run under a budget until the
|
|
@@ -805,6 +811,22 @@ interface SendOptions$1 extends ModelChoiceOptions {
|
|
|
805
811
|
* The `image_mode` field itself is accepted from Astralform ≥ 0.59.0.
|
|
806
812
|
*/
|
|
807
813
|
imageMode?: boolean;
|
|
814
|
+
/**
|
|
815
|
+
* Put this turn in video mode, attaching the video-generation tool.
|
|
816
|
+
*
|
|
817
|
+
* Off by default and per-message, for a sharper reason than images: a clip
|
|
818
|
+
* occupies one shared GPU for minutes, during which image generation on the
|
|
819
|
+
* same host cannot run at all. The tool is not attached unless this is set.
|
|
820
|
+
*
|
|
821
|
+
* Mutually exclusive with `imageMode` at the composer level — which is why a
|
|
822
|
+
* video turn also gets the image tool server-side: the user cannot select
|
|
823
|
+
* both, so the agent must be able to produce its own first frame.
|
|
824
|
+
*
|
|
825
|
+
* `generate_video` animates an EXISTING image; it cannot start from text, and
|
|
826
|
+
* the clip is silent. Gate the affordance on `AgentStatus.capabilities`
|
|
827
|
+
* (`video`), the same way image mode does.
|
|
828
|
+
*/
|
|
829
|
+
videoMode?: boolean;
|
|
808
830
|
/**
|
|
809
831
|
* Start a durable long-horizon goal for this run (goal mode). The text becomes
|
|
810
832
|
* the goal's objective; the backend keeps the agent working under a budget until
|
|
@@ -1158,6 +1180,11 @@ declare class ChatSession {
|
|
|
1158
1180
|
* Consume a single SSE stream to exhaustion. Returns whether a terminal
|
|
1159
1181
|
* event (``message_stop`` / ``error``) was seen, so the caller can decide
|
|
1160
1182
|
* whether an ended stream means "turn done" vs "dropped, reconnect".
|
|
1183
|
+
*
|
|
1184
|
+
* ``onStall`` aborts the per-attempt connection: if no event arrives within
|
|
1185
|
+
* SSE_STALL_TIMEOUT_MS (backend keepalives land every 15s), the stream is a
|
|
1186
|
+
* zombie — ``reader.read()`` will never settle — so we kill the fetch and
|
|
1187
|
+
* throw a ConnectionError, feeding the caller's reconnect-from-lastSeq loop.
|
|
1161
1188
|
*/
|
|
1162
1189
|
private pumpStream;
|
|
1163
1190
|
/** Sleep for ``ms``, resolving early if the turn is aborted mid-backoff. */
|
|
@@ -1334,6 +1361,13 @@ interface SendOptions extends ModelChoiceOptions {
|
|
|
1334
1361
|
* provider. Gate the affordance on `AgentStatus.capabilities`.
|
|
1335
1362
|
*/
|
|
1336
1363
|
imageMode?: boolean;
|
|
1364
|
+
/**
|
|
1365
|
+
* Attach the video-generation tool to this turn. Mutually exclusive with
|
|
1366
|
+
* `imageMode` at the composer level. See `SendOptions.videoMode` in types.ts
|
|
1367
|
+
* for the full rule — a clip animates an existing image, is silent, and holds
|
|
1368
|
+
* one shared GPU for minutes, so it is per-message and off by default.
|
|
1369
|
+
*/
|
|
1370
|
+
videoMode?: boolean;
|
|
1337
1371
|
/**
|
|
1338
1372
|
* Start a durable long-horizon goal for this run (goal mode) — the text is the
|
|
1339
1373
|
* goal objective the backend drives to completion. Omit for a normal turn.
|
package/dist/index.js
CHANGED
|
@@ -737,7 +737,8 @@ var AstralformClient = class {
|
|
|
737
737
|
name: a.name,
|
|
738
738
|
teamId: a.team_id,
|
|
739
739
|
createdAt: a.created_at,
|
|
740
|
-
updatedAt: a.updated_at
|
|
740
|
+
updatedAt: a.updated_at,
|
|
741
|
+
avatarUrl: a.avatar_url ?? null
|
|
741
742
|
}));
|
|
742
743
|
}
|
|
743
744
|
// --- Jobs API ---
|
|
@@ -1019,6 +1020,19 @@ function translateAgentIdentity(raw) {
|
|
|
1019
1020
|
description: raw.description ?? null
|
|
1020
1021
|
};
|
|
1021
1022
|
}
|
|
1023
|
+
function translateTodoItem(raw) {
|
|
1024
|
+
return {
|
|
1025
|
+
id: raw.id ?? 0,
|
|
1026
|
+
subject: raw.subject ?? "",
|
|
1027
|
+
status: raw.status ?? "pending",
|
|
1028
|
+
description: raw.description ?? null,
|
|
1029
|
+
activeForm: raw.active_form ?? null,
|
|
1030
|
+
owner: raw.owner ?? null,
|
|
1031
|
+
blockedBy: raw.blocked_by ?? null,
|
|
1032
|
+
blocks: raw.blocks ?? null,
|
|
1033
|
+
priority: raw.priority ?? null
|
|
1034
|
+
};
|
|
1035
|
+
}
|
|
1022
1036
|
function translateCustomEvent(name, data) {
|
|
1023
1037
|
switch (name) {
|
|
1024
1038
|
case "user_message":
|
|
@@ -1035,7 +1049,9 @@ function translateCustomEvent(name, data) {
|
|
|
1035
1049
|
case "todo_update":
|
|
1036
1050
|
return {
|
|
1037
1051
|
type: "todo_update",
|
|
1038
|
-
todos: data.todos ?? []
|
|
1052
|
+
todos: (data.todos ?? []).map(
|
|
1053
|
+
(t) => translateTodoItem(t)
|
|
1054
|
+
)
|
|
1039
1055
|
};
|
|
1040
1056
|
case "plan_update":
|
|
1041
1057
|
return {
|
|
@@ -1228,7 +1244,8 @@ function translateWireEvent(wire) {
|
|
|
1228
1244
|
usage: {
|
|
1229
1245
|
inputTokens: wire.usage.input_tokens ?? 0,
|
|
1230
1246
|
outputTokens: wire.usage.output_tokens ?? 0,
|
|
1231
|
-
cachedTokens: wire.usage.cached_tokens ?? 0
|
|
1247
|
+
cachedTokens: wire.usage.cached_tokens ?? 0,
|
|
1248
|
+
cacheCreationTokens: wire.usage.cache_creation_tokens ?? 0
|
|
1232
1249
|
},
|
|
1233
1250
|
ttfbMs: wire.ttfb_ms,
|
|
1234
1251
|
totalMs: wire.total_ms,
|
|
@@ -1274,6 +1291,7 @@ function translateWireEvent(wire) {
|
|
|
1274
1291
|
|
|
1275
1292
|
// src/session.ts
|
|
1276
1293
|
var SSE_MAX_RECONNECTS = 6;
|
|
1294
|
+
var SSE_STALL_TIMEOUT_MS = 45e3;
|
|
1277
1295
|
var TOOL_RESULT_MAX_RETRIES = 3;
|
|
1278
1296
|
var CONVERSATION_PAGE_SIZE = 50;
|
|
1279
1297
|
function sseReconnectDelayMs(attempt) {
|
|
@@ -1436,6 +1454,7 @@ var ChatSession = class {
|
|
|
1436
1454
|
agent_name: options?.agentName,
|
|
1437
1455
|
plan_mode: options?.planMode,
|
|
1438
1456
|
image_mode: options?.imageMode,
|
|
1457
|
+
video_mode: options?.videoMode,
|
|
1439
1458
|
goal: options?.goal,
|
|
1440
1459
|
// Per-request model choice (client-side model selection).
|
|
1441
1460
|
provider: options?.provider,
|
|
@@ -1524,14 +1543,23 @@ var ChatSession = class {
|
|
|
1524
1543
|
async consumeEventStream(jobId, conversationId, messageId, executeClientTools) {
|
|
1525
1544
|
const signal = this.abortController?.signal;
|
|
1526
1545
|
for (let attempt = 0; ; attempt++) {
|
|
1527
|
-
|
|
1546
|
+
if (signal?.aborted) return;
|
|
1547
|
+
const attemptController = new AbortController();
|
|
1548
|
+
const linkAbort = () => attemptController.abort();
|
|
1549
|
+
signal?.addEventListener("abort", linkAbort);
|
|
1528
1550
|
let sawTerminal;
|
|
1529
1551
|
try {
|
|
1552
|
+
const stream = this.client.streamJobEvents(
|
|
1553
|
+
jobId,
|
|
1554
|
+
this.lastSeq,
|
|
1555
|
+
attemptController.signal
|
|
1556
|
+
);
|
|
1530
1557
|
sawTerminal = await this.pumpStream(
|
|
1531
1558
|
stream,
|
|
1532
1559
|
conversationId,
|
|
1533
1560
|
messageId,
|
|
1534
|
-
executeClientTools
|
|
1561
|
+
executeClientTools,
|
|
1562
|
+
() => attemptController.abort()
|
|
1535
1563
|
);
|
|
1536
1564
|
} catch (err) {
|
|
1537
1565
|
if (signal?.aborted) return;
|
|
@@ -1541,6 +1569,8 @@ var ChatSession = class {
|
|
|
1541
1569
|
if (attempt >= SSE_MAX_RECONNECTS) throw err;
|
|
1542
1570
|
await this.sleepUnlessAborted(sseReconnectDelayMs(attempt + 1), signal);
|
|
1543
1571
|
continue;
|
|
1572
|
+
} finally {
|
|
1573
|
+
signal?.removeEventListener("abort", linkAbort);
|
|
1544
1574
|
}
|
|
1545
1575
|
if (sawTerminal || signal?.aborted) return;
|
|
1546
1576
|
if (attempt >= SSE_MAX_RECONNECTS) {
|
|
@@ -1553,35 +1583,66 @@ var ChatSession = class {
|
|
|
1553
1583
|
* Consume a single SSE stream to exhaustion. Returns whether a terminal
|
|
1554
1584
|
* event (``message_stop`` / ``error``) was seen, so the caller can decide
|
|
1555
1585
|
* whether an ended stream means "turn done" vs "dropped, reconnect".
|
|
1586
|
+
*
|
|
1587
|
+
* ``onStall`` aborts the per-attempt connection: if no event arrives within
|
|
1588
|
+
* SSE_STALL_TIMEOUT_MS (backend keepalives land every 15s), the stream is a
|
|
1589
|
+
* zombie — ``reader.read()`` will never settle — so we kill the fetch and
|
|
1590
|
+
* throw a ConnectionError, feeding the caller's reconnect-from-lastSeq loop.
|
|
1556
1591
|
*/
|
|
1557
|
-
async pumpStream(stream, conversationId, messageId, executeClientTools) {
|
|
1592
|
+
async pumpStream(stream, conversationId, messageId, executeClientTools, onStall) {
|
|
1558
1593
|
let sawTerminal = false;
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
const
|
|
1563
|
-
|
|
1564
|
-
|
|
1594
|
+
const iterator = stream[Symbol.asyncIterator]();
|
|
1595
|
+
try {
|
|
1596
|
+
while (true) {
|
|
1597
|
+
const next = iterator.next();
|
|
1598
|
+
let stallTimer;
|
|
1599
|
+
const stall = new Promise((_, reject) => {
|
|
1600
|
+
stallTimer = setTimeout(() => {
|
|
1601
|
+
reject(
|
|
1602
|
+
new ConnectionError(
|
|
1603
|
+
`Stream stalled: no events for ${SSE_STALL_TIMEOUT_MS}ms`
|
|
1604
|
+
)
|
|
1605
|
+
);
|
|
1606
|
+
onStall?.();
|
|
1607
|
+
}, SSE_STALL_TIMEOUT_MS);
|
|
1608
|
+
});
|
|
1609
|
+
let result;
|
|
1610
|
+
try {
|
|
1611
|
+
result = await Promise.race([next, stall]);
|
|
1612
|
+
} finally {
|
|
1613
|
+
clearTimeout(stallTimer);
|
|
1614
|
+
}
|
|
1615
|
+
if (result.done) break;
|
|
1616
|
+
const raw = result.value;
|
|
1617
|
+
let parsed;
|
|
1618
|
+
try {
|
|
1619
|
+
const data = JSON.parse(raw.data);
|
|
1620
|
+
if (typeof data !== "object" || data === null || typeof data.type !== "string") {
|
|
1621
|
+
if (typeof data?.seq === "number") {
|
|
1622
|
+
this.lastSeq = data.seq;
|
|
1623
|
+
}
|
|
1624
|
+
continue;
|
|
1625
|
+
}
|
|
1626
|
+
parsed = data;
|
|
1627
|
+
if (typeof data.seq === "number") {
|
|
1565
1628
|
this.lastSeq = data.seq;
|
|
1566
1629
|
}
|
|
1630
|
+
} catch {
|
|
1567
1631
|
continue;
|
|
1568
1632
|
}
|
|
1569
|
-
parsed
|
|
1570
|
-
|
|
1571
|
-
this.lastSeq = data.seq;
|
|
1633
|
+
if (parsed.type === "message_stop" || parsed.type === "error") {
|
|
1634
|
+
sawTerminal = true;
|
|
1572
1635
|
}
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1636
|
+
await this.dispatchWireEvent(
|
|
1637
|
+
parsed,
|
|
1638
|
+
conversationId,
|
|
1639
|
+
messageId,
|
|
1640
|
+
executeClientTools
|
|
1641
|
+
);
|
|
1578
1642
|
}
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
messageId,
|
|
1583
|
-
executeClientTools
|
|
1584
|
-
);
|
|
1643
|
+
} finally {
|
|
1644
|
+
void iterator.return?.(void 0).catch(() => {
|
|
1645
|
+
});
|
|
1585
1646
|
}
|
|
1586
1647
|
return sawTerminal;
|
|
1587
1648
|
}
|
|
@@ -2143,6 +2204,7 @@ var StreamManager = class {
|
|
|
2143
2204
|
uploadIds: options?.uploadIds,
|
|
2144
2205
|
planMode: options?.planMode,
|
|
2145
2206
|
imageMode: options?.imageMode,
|
|
2207
|
+
videoMode: options?.videoMode,
|
|
2146
2208
|
goal: options?.goal,
|
|
2147
2209
|
provider: options?.provider,
|
|
2148
2210
|
model: options?.model,
|