@stratusagent/core 0.11.6 → 0.11.7
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 +495 -16
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +782 -89
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -169,6 +169,44 @@ export interface Message {
|
|
|
169
169
|
* — the boundary provenance draws, here at the point text enters.
|
|
170
170
|
*/
|
|
171
171
|
overheard?: boolean;
|
|
172
|
+
/**
|
|
173
|
+
* Appended by `observe`, so it started no turn. `overheard` alone cannot
|
|
174
|
+
* say so: a turn dispatched with `addressed: false` is overheard too, and
|
|
175
|
+
* is a turn. Present only when true; a message observed before this was
|
|
176
|
+
* recorded reads as a turn, which errs toward attributing nothing to an
|
|
177
|
+
* earlier one (`isLatestTurn`).
|
|
178
|
+
*/
|
|
179
|
+
observed?: boolean;
|
|
180
|
+
/**
|
|
181
|
+
* The caller's idempotency key for the dispatch that brought this user
|
|
182
|
+
* message (`RunInput.idempotencyKey`). Written in the same save as the
|
|
183
|
+
* message, so a key is never durable without its message or the other
|
|
184
|
+
* way round; `workItemState` reads it back.
|
|
185
|
+
*/
|
|
186
|
+
idempotencyKey?: string;
|
|
187
|
+
/**
|
|
188
|
+
* Why the turn this user message started failed, written by
|
|
189
|
+
* `recordTurnFailure` in the save that fails it. The session's
|
|
190
|
+
* `lastError` is the latest turn's and the next turn replaces it; this
|
|
191
|
+
* stays, so `turnFailureFor` can answer for any turn a key names.
|
|
192
|
+
*/
|
|
193
|
+
turnError?: string;
|
|
194
|
+
/**
|
|
195
|
+
* This message's turn was continued once after the process running it
|
|
196
|
+
* died (`AgentRunner.continueTurn`). A host fails a second such turn
|
|
197
|
+
* instead of continuing it again: a turn that takes the process down
|
|
198
|
+
* with it would otherwise do so on every start. Present only when true.
|
|
199
|
+
*/
|
|
200
|
+
continuedAfterCrash?: boolean;
|
|
201
|
+
/**
|
|
202
|
+
* The turn this message started could reach a provider that runs its own
|
|
203
|
+
* tool loop (a harness): what that turn did is not all in this
|
|
204
|
+
* transcript, so a crash mid-turn cannot be continued from it. Recorded
|
|
205
|
+
* at accept time, because the runtime a crashed turn ran on is not the
|
|
206
|
+
* one a restart resolves if the configuration changed in between.
|
|
207
|
+
* Present only when true; see `RunInput.hostedLoop`.
|
|
208
|
+
*/
|
|
209
|
+
hostedLoop?: boolean;
|
|
172
210
|
}
|
|
173
211
|
export interface AgentDescriptor {
|
|
174
212
|
id: string;
|
|
@@ -369,6 +407,38 @@ export interface ToolResult {
|
|
|
369
407
|
*/
|
|
370
408
|
trust?: TrustLevel;
|
|
371
409
|
}
|
|
410
|
+
/**
|
|
411
|
+
* What the model is told about a result labelled `external`, said once
|
|
412
|
+
* here rather than left for each tool to word — a third-party plugin or an
|
|
413
|
+
* MCP server never has to know the convention for its output to carry it.
|
|
414
|
+
*/
|
|
415
|
+
export declare const UNTRUSTED_TOOL_RESULT_NOTE: string;
|
|
416
|
+
/**
|
|
417
|
+
* The text a provider hands the model for a tool result — the one
|
|
418
|
+
* serialization every provider and harness bridge uses, so the trust label
|
|
419
|
+
* reaches the model the same way whichever one carries the turn.
|
|
420
|
+
*
|
|
421
|
+
* An `external` result is wrapped in `{ untrusted, untrustedNote, output }`,
|
|
422
|
+
* the vocabulary `web.search` already used in-band. This marks the boundary
|
|
423
|
+
* and nothing more: a model can still follow text inside the envelope, and
|
|
424
|
+
* it is not a prompt-injection defence. Only `external` is wrapped, not
|
|
425
|
+
* `unknown`: `shell.run` declares `unknown`, and a marker on every command's
|
|
426
|
+
* output would teach the model that the marker is noise. A failed result is
|
|
427
|
+
* wrapped too, because the executor labels failures — a fetched server's
|
|
428
|
+
* error text is that server's text.
|
|
429
|
+
*
|
|
430
|
+
* The envelope is applied whatever the output already says, including
|
|
431
|
+
* `web.search`'s own `untrusted: true`. Skipping on a field of the output
|
|
432
|
+
* would let the output decide whether it is marked, and the output is the
|
|
433
|
+
* party not trusted; the repetition costs a sentence.
|
|
434
|
+
*
|
|
435
|
+
* Never mutates the result: the transcript, the dashboard, and the control
|
|
436
|
+
* API read the raw output. Deterministic for a given result, so a replayed
|
|
437
|
+
* transcript is byte-identical and the prompt cache holds. `Tool failed`
|
|
438
|
+
* stays unpunctuated because the API providers replay it from persisted
|
|
439
|
+
* transcripts, where a changed byte is a cache miss.
|
|
440
|
+
*/
|
|
441
|
+
export declare const renderToolResultContent: (result: Pick<ToolResult, "ok" | "output" | "error" | "trust">) => string;
|
|
372
442
|
export type ProviderPart = {
|
|
373
443
|
type: 'text';
|
|
374
444
|
text: string;
|
|
@@ -379,7 +449,8 @@ export type ProviderPart = {
|
|
|
379
449
|
/**
|
|
380
450
|
* A streamed fragment of an in-progress provider response. Text deltas carry
|
|
381
451
|
* output as it is generated; tool-call deltas announce that the model has
|
|
382
|
-
* started emitting a call (input may still be incomplete)
|
|
452
|
+
* started emitting a call (input may still be incomplete); thinking and
|
|
453
|
+
* progress deltas carry nothing but the fact of activity. A reset delta
|
|
383
454
|
* tells consumers to DISCARD every fragment streamed so far for this
|
|
384
455
|
* response — emitted when a provider abandons a partial attempt (e.g. a
|
|
385
456
|
* fallback wrapper retrying after the primary failed mid-stream), so
|
|
@@ -400,6 +471,16 @@ export type ProviderDelta = {
|
|
|
400
471
|
*/
|
|
401
472
|
| {
|
|
402
473
|
type: 'thinking';
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* The provider is working on this response and has nothing to show for
|
|
477
|
+
* it yet: a retry it is waiting out, a context compaction, a message
|
|
478
|
+
* boundary. Content-free like `thinking`, and for the same reader — an
|
|
479
|
+
* activity watchdog that would otherwise take a busy provider for a
|
|
480
|
+
* stalled one. A renderer has nothing to draw for it.
|
|
481
|
+
*/
|
|
482
|
+
| {
|
|
483
|
+
type: 'progress';
|
|
403
484
|
} | {
|
|
404
485
|
type: 'reset';
|
|
405
486
|
/**
|
|
@@ -1391,6 +1472,13 @@ export declare class InMemoryAgentMemoryStore implements AgentMemoryStore {
|
|
|
1391
1472
|
* Resolves named credentials for an agent. Implementations must enforce the
|
|
1392
1473
|
* agent's `credentials` allowlist so secrets stay scoped per agent.
|
|
1393
1474
|
*/
|
|
1475
|
+
/** Whose a named credential is: one agent's own, or the whole fleet's. */
|
|
1476
|
+
export type CredentialScope = 'agent' | 'shared';
|
|
1477
|
+
/**
|
|
1478
|
+
* How a credential request reaches a person: a form in the conversation's
|
|
1479
|
+
* own channel, or a one-time link the agent passes on.
|
|
1480
|
+
*/
|
|
1481
|
+
export type CredentialDelivery = 'form' | 'link';
|
|
1394
1482
|
export interface CredentialResolver {
|
|
1395
1483
|
resolve(agent: AgentDefinition, name: string): Promise<string | undefined>;
|
|
1396
1484
|
}
|
|
@@ -1499,6 +1587,99 @@ export declare const uncachedInputTokens: (total: number, ...cached: Array<numbe
|
|
|
1499
1587
|
* nothing at all.
|
|
1500
1588
|
*/
|
|
1501
1589
|
export declare const totalTokenUsage: (records: readonly TokenUsage[]) => TokenUsage | undefined;
|
|
1590
|
+
/**
|
|
1591
|
+
* Facts about how an agent is run that only the host knows, told to the
|
|
1592
|
+
* agent so it does not have to guess them.
|
|
1593
|
+
*
|
|
1594
|
+
* It does guess otherwise, and guesses wrong: an agent asked to reread its
|
|
1595
|
+
* soul after an edit answered that it had none, because there was no
|
|
1596
|
+
* `SOUL.md` in its workspace, which is where other agent runtimes keep one.
|
|
1597
|
+
* Handed in by the host rather than written into the soul because the
|
|
1598
|
+
* paths are the host's to own, and a copy in every soul would go stale the
|
|
1599
|
+
* next time the layout moved, the way the workspace already did once.
|
|
1600
|
+
*/
|
|
1601
|
+
export interface AgentRuntimeContext {
|
|
1602
|
+
/** Absolute path of the soul file this agent's persona was loaded from. */
|
|
1603
|
+
soulPath?: string;
|
|
1604
|
+
/**
|
|
1605
|
+
* True when the host reads the soul again before every turn, so an edit
|
|
1606
|
+
* reaches the agent's next reply. The daemon does; a one-shot run reads
|
|
1607
|
+
* it once, and leaving this out says nothing either way.
|
|
1608
|
+
*/
|
|
1609
|
+
soulReloads?: boolean;
|
|
1610
|
+
/** Where this agent's files go: `AgentWorkspaces.forAgent`. */
|
|
1611
|
+
workspace?: string;
|
|
1612
|
+
/**
|
|
1613
|
+
* The language the agent writes in, as a tag `isLanguageTag` accepts,
|
|
1614
|
+
* already resolved by the host: the soul's `language:`, then the config
|
|
1615
|
+
* file's. Absent means {@link DEFAULT_LANGUAGE}. Rendered in the reply
|
|
1616
|
+
* section rather than here, so a host that says nothing else still gets
|
|
1617
|
+
* the default rule.
|
|
1618
|
+
*/
|
|
1619
|
+
language?: string;
|
|
1620
|
+
/**
|
|
1621
|
+
* What the host has configured to answer as this agent, and what it has
|
|
1622
|
+
* observed: `onFallback` is true once this conversation has switched to
|
|
1623
|
+
* the fallback, which is sticky, so a model asked what it is can tell a
|
|
1624
|
+
* configured default from the one actually answering. The fallback
|
|
1625
|
+
* wrapper sets it on the request it hands the fallback mid-turn, so the
|
|
1626
|
+
* model that took over is not told it is the default.
|
|
1627
|
+
*/
|
|
1628
|
+
model?: AgentModelContext;
|
|
1629
|
+
/**
|
|
1630
|
+
* Who can read this conversation, as the channel that carried the turn
|
|
1631
|
+
* reported it on the turn's metadata (`conversationContextFrom`). Per
|
|
1632
|
+
* turn rather than read off the session, so a thread that started before
|
|
1633
|
+
* this existed, or a channel that grew since, is described as it is now.
|
|
1634
|
+
*/
|
|
1635
|
+
conversation?: ConversationContext;
|
|
1636
|
+
}
|
|
1637
|
+
/**
|
|
1638
|
+
* What kind of room a conversation is: one person, a few, a closed
|
|
1639
|
+
* channel, or one the whole workspace can read. An agent that did not
|
|
1640
|
+
* know told a person in a Slack DM they were "talking on the terminal",
|
|
1641
|
+
* and would have posted the same reply to a thousand-person channel.
|
|
1642
|
+
*/
|
|
1643
|
+
export type ConversationKind = 'direct' | 'group' | 'private' | 'public';
|
|
1644
|
+
export interface ConversationContext {
|
|
1645
|
+
kind: ConversationKind;
|
|
1646
|
+
/** Members as the channel counts them, the agent included. */
|
|
1647
|
+
members?: number;
|
|
1648
|
+
/**
|
|
1649
|
+
* Who a direct message is with. It reaches the system prompt, so a
|
|
1650
|
+
* channel sets it only for someone the operator vouched for (a Slack
|
|
1651
|
+
* principal), never from a name anyone could have typed.
|
|
1652
|
+
*/
|
|
1653
|
+
with?: string;
|
|
1654
|
+
/** The turn is a reply in a thread rather than the channel itself. */
|
|
1655
|
+
thread?: boolean;
|
|
1656
|
+
/**
|
|
1657
|
+
* People outside this workspace read it too: a Slack Connect channel,
|
|
1658
|
+
* or one shared across an organization's workspaces. Never set for a
|
|
1659
|
+
* direct message.
|
|
1660
|
+
*/
|
|
1661
|
+
shared?: boolean;
|
|
1662
|
+
}
|
|
1663
|
+
/**
|
|
1664
|
+
* The metadata key a channel adapter records a turn's room under: a
|
|
1665
|
+
* `ConversationContext`-shaped object, read back by
|
|
1666
|
+
* `conversationContextFrom`.
|
|
1667
|
+
*/
|
|
1668
|
+
export declare const CONVERSATION_METADATA_KEY = "conversation";
|
|
1669
|
+
/**
|
|
1670
|
+
* The room a turn's metadata describes, keeping only what is safe to put
|
|
1671
|
+
* in front of the model, or undefined when it describes none.
|
|
1672
|
+
*/
|
|
1673
|
+
export declare const conversationContextFrom: (metadata: JsonObject | undefined) => ConversationContext | undefined;
|
|
1674
|
+
export interface AgentModelContext {
|
|
1675
|
+
provider: string;
|
|
1676
|
+
model?: string;
|
|
1677
|
+
fallback?: {
|
|
1678
|
+
provider: string;
|
|
1679
|
+
model: string;
|
|
1680
|
+
};
|
|
1681
|
+
onFallback?: boolean;
|
|
1682
|
+
}
|
|
1502
1683
|
export interface ProviderRequest {
|
|
1503
1684
|
/**
|
|
1504
1685
|
* The live session. **Persistable**: a provider that saves it, or hands
|
|
@@ -1551,6 +1732,12 @@ export interface ProviderRequest {
|
|
|
1551
1732
|
* the model decides a description is relevant.
|
|
1552
1733
|
*/
|
|
1553
1734
|
skills?: SkillDescriptor[];
|
|
1735
|
+
/**
|
|
1736
|
+
* What the host can say about how this agent is run: where its soul and
|
|
1737
|
+
* workspace are. Rendered as the `runtime` section; see
|
|
1738
|
+
* {@link AgentRuntimeContext}.
|
|
1739
|
+
*/
|
|
1740
|
+
runtime?: AgentRuntimeContext;
|
|
1554
1741
|
/**
|
|
1555
1742
|
* Streaming sink. Adapters that stream call this per fragment and MUST
|
|
1556
1743
|
* await the returned promise before the next call (backpressure); the
|
|
@@ -1842,6 +2029,82 @@ export interface PendingApprovalRecord {
|
|
|
1842
2029
|
* cannot disagree on which message is "the reply".
|
|
1843
2030
|
*/
|
|
1844
2031
|
export declare const latestTurnReply: (session: Pick<Session, "messages">) => string | undefined;
|
|
2032
|
+
/**
|
|
2033
|
+
* Whether a session's transcript already ends in the turn's answer: its
|
|
2034
|
+
* last response holds no tool call, so the loop owed the model nothing more
|
|
2035
|
+
* and only the save marking the turn completed is missing. A continuation
|
|
2036
|
+
* then completes it without a provider call.
|
|
2037
|
+
*
|
|
2038
|
+
* Judged on the last response as a whole: a response's parts are saved
|
|
2039
|
+
* together and its tool results after them all, so the trailing run of
|
|
2040
|
+
* assistant messages is that one response. Any call in it — even one
|
|
2041
|
+
* followed by text, which a provider may send in that order — means the
|
|
2042
|
+
* model is owed another turn. Read it before `reconcileInterruptedToolCalls`
|
|
2043
|
+
* splices results in.
|
|
2044
|
+
*/
|
|
2045
|
+
export declare const lastResponseIsAnswer: (session: Pick<Session, "messages">) => boolean;
|
|
2046
|
+
/**
|
|
2047
|
+
* The end of a turn that answered: marked completed in one save, then
|
|
2048
|
+
* announced. The runner's loop ends here, and so does a turn whose answer
|
|
2049
|
+
* a process saved and then died before marking it completed — which needs
|
|
2050
|
+
* no provider and no current agent definition to finish, so a host can do
|
|
2051
|
+
* it with only its store and bus (`lastResponseIsAnswer` says when).
|
|
2052
|
+
*/
|
|
2053
|
+
export declare const completeAnsweredTurn: (finished: Session, store: SessionStore, bus: EventBus) => Promise<Session>;
|
|
2054
|
+
/**
|
|
2055
|
+
* The longest idempotency key a dispatch accepts. Here rather than in the
|
|
2056
|
+
* gateway that enforces it, because a channel composing keys from ids it
|
|
2057
|
+
* does not bound (an agent's, a platform's) has to fit them to it.
|
|
2058
|
+
*/
|
|
2059
|
+
export declare const MAX_IDEMPOTENCY_KEY_LENGTH = 256;
|
|
2060
|
+
/**
|
|
2061
|
+
* The reply of the turn a keyed message started (`Message.idempotencyKey`),
|
|
2062
|
+
* where `latestTurnReply` is the newest turn's. A repeated dispatch resolves
|
|
2063
|
+
* with the session as it stands, which may have moved on since: an adapter
|
|
2064
|
+
* answering a redelivered message reads the reply here, or it would answer
|
|
2065
|
+
* the old message with a newer turn's words. Undefined when no message
|
|
2066
|
+
* carries the key, or its turn produced no text.
|
|
2067
|
+
*/
|
|
2068
|
+
export declare const turnReplyFor: (session: Pick<Session, "messages">, idempotencyKey: string) => string | undefined;
|
|
2069
|
+
/**
|
|
2070
|
+
* The files the turn a keyed message started produced (`filePathsOf` over
|
|
2071
|
+
* its tool results), the other half of what `turnReplyFor` reads. A
|
|
2072
|
+
* channel uploads a turn's files as the results arrive, so an adapter
|
|
2073
|
+
* answering a repeat of a finished turn — no events, only the session —
|
|
2074
|
+
* has this and nothing else to find them by.
|
|
2075
|
+
*
|
|
2076
|
+
* A path, not the bytes: the transcript never held them. `producedAt` is
|
|
2077
|
+
* when the result naming it was recorded, so a reader can refuse a path
|
|
2078
|
+
* that has been written since — by now it may hold a later turn's file.
|
|
2079
|
+
*/
|
|
2080
|
+
export declare const turnFilesFor: (session: Pick<Session, "messages">, idempotencyKey: string) => Array<{
|
|
2081
|
+
path: string;
|
|
2082
|
+
producedAt: string;
|
|
2083
|
+
}>;
|
|
2084
|
+
/**
|
|
2085
|
+
* Fail a session's in-flight turn: `status` and `lastError`, and the error
|
|
2086
|
+
* on the turn's own user message (`Message.turnError`), where the next
|
|
2087
|
+
* turn cannot overwrite it. The one write every path that fails a turn
|
|
2088
|
+
* makes — the runner's and the gateway's sweeps — so `turnFailureFor`
|
|
2089
|
+
* reads the same record whichever failed it. In flight, the turn's message
|
|
2090
|
+
* is the session's newest user message (`observe` refuses to append).
|
|
2091
|
+
*/
|
|
2092
|
+
export declare const recordTurnFailure: (session: Session, error: string) => void;
|
|
2093
|
+
/**
|
|
2094
|
+
* Whether the keyed message started the session's latest turn. A message
|
|
2095
|
+
* `observe` appended since started no turn and is passed over
|
|
2096
|
+
* (`Message.observed`); every other user message, keyed or not, addressed
|
|
2097
|
+
* or not, started one.
|
|
2098
|
+
*/
|
|
2099
|
+
export declare const isLatestTurn: (session: Pick<Session, "messages">, idempotencyKey: string) => boolean;
|
|
2100
|
+
/**
|
|
2101
|
+
* Why the turn a keyed message started failed, or undefined if it did not.
|
|
2102
|
+
* Read from the message (`recordTurnFailure`), so a turn the session has
|
|
2103
|
+
* moved on from still answers. A transcript written before turns carried
|
|
2104
|
+
* their own failure has only the session's, which is the keyed turn's
|
|
2105
|
+
* while it is the latest (`isLatestTurn`).
|
|
2106
|
+
*/
|
|
2107
|
+
export declare const turnFailureFor: (session: Pick<Session, "messages" | "status" | "lastError">, idempotencyKey: string) => string | undefined;
|
|
1845
2108
|
/**
|
|
1846
2109
|
* A user message's text as a prompt should carry it. The one place an
|
|
1847
2110
|
* overheard message is framed, so the API providers' per-message blocks
|
|
@@ -1877,6 +2140,22 @@ export declare const promptTextOf: (message: Pick<Message, "content" | "overhear
|
|
|
1877
2140
|
* as the answer rather than as a broken endpoint.
|
|
1878
2141
|
*/
|
|
1879
2142
|
export declare const isUnaddressedTurn: (session: Pick<Session, "messages">) => boolean;
|
|
2143
|
+
/**
|
|
2144
|
+
* Where the dispatch carrying `idempotencyKey` stands in this session:
|
|
2145
|
+
* `undefined` if no message carries the key, `unfinished` if its message
|
|
2146
|
+
* is the one the session's in-flight turn is on, `finished` otherwise.
|
|
2147
|
+
*
|
|
2148
|
+
* Derived, never recorded separately, which is what makes it exact. The
|
|
2149
|
+
* keyed message is written in the same save that sets the status to
|
|
2150
|
+
* `running` (accepted and started are one write), and the save that moves
|
|
2151
|
+
* the status off `running` or `pending_approval` is the turn's final one,
|
|
2152
|
+
* so `finished` is written in the same transaction as the final save by
|
|
2153
|
+
* construction. While a turn is in flight its message is the session's
|
|
2154
|
+
* newest user message — `observe` refuses to append during one, and the
|
|
2155
|
+
* runner's wrap-up notes never reach the store — the same reading
|
|
2156
|
+
* `isUnaddressedTurn` relies on.
|
|
2157
|
+
*/
|
|
2158
|
+
export declare const workItemState: (session: Pick<Session, "messages" | "status">, idempotencyKey: string) => "unfinished" | "finished" | undefined;
|
|
1880
2159
|
export declare const markPromptDelivered: <T>(error: T) => T;
|
|
1881
2160
|
export declare const promptWasDelivered: (error: unknown) => boolean;
|
|
1882
2161
|
export interface PromptTextOptions {
|
|
@@ -2069,6 +2348,54 @@ export type StratusEvent = {
|
|
|
2069
2348
|
reason: ApprovalResolutionReason;
|
|
2070
2349
|
/** Who decided, when a person did. Channel-native id (a Slack user). */
|
|
2071
2350
|
actor?: string;
|
|
2351
|
+
}
|
|
2352
|
+
/**
|
|
2353
|
+
* An agent asked for a named credential it does not hold
|
|
2354
|
+
* (`credential.request`), and it has already been put to someone: a form
|
|
2355
|
+
* in the conversation it asked in (`via: 'form'`), or a one-time link the
|
|
2356
|
+
* agent was handed to pass on (`via: 'link'`). The request is delivered
|
|
2357
|
+
* first and announced after, so this is a record of a question that was
|
|
2358
|
+
* asked, never a request for someone to render one. Nothing about it is
|
|
2359
|
+
* a secret, and nothing that answers it ever travels on the bus. `scope` is whose the key would be: the
|
|
2360
|
+
* agent's own (`agent`, the default) or the fleet's (`shared`).
|
|
2361
|
+
*
|
|
2362
|
+
* `metadata` is the session's, for the same reason an approval request
|
|
2363
|
+
* carries it: it says which conversation to ask in.
|
|
2364
|
+
*/
|
|
2365
|
+
| {
|
|
2366
|
+
type: 'credential.requested';
|
|
2367
|
+
sessionId: string;
|
|
2368
|
+
agentId: string;
|
|
2369
|
+
requestId: string;
|
|
2370
|
+
name: string;
|
|
2371
|
+
scope: CredentialScope;
|
|
2372
|
+
/** The agent's own words on what it needs the key for. Untrusted text. */
|
|
2373
|
+
reason?: string;
|
|
2374
|
+
/** How it was put to someone. Absent from hosts before links existed, which only had forms. */
|
|
2375
|
+
via?: CredentialDelivery;
|
|
2376
|
+
metadata?: JsonObject;
|
|
2377
|
+
}
|
|
2378
|
+
/**
|
|
2379
|
+
* A requested credential was stored for the agent that asked, and granted
|
|
2380
|
+
* to it. Names only; the value went to the credential store and nowhere
|
|
2381
|
+
* else.
|
|
2382
|
+
*
|
|
2383
|
+
* Emitted once the key is stored, whether or not the grant then landed:
|
|
2384
|
+
* a key added from a chat is a change on disk, and a shared one is
|
|
2385
|
+
* already usable by every agent granted that name, so it is recorded even
|
|
2386
|
+
* when the soul write failed. `grantError` says it did, and why.
|
|
2387
|
+
*/
|
|
2388
|
+
| {
|
|
2389
|
+
type: 'credential.provided';
|
|
2390
|
+
sessionId: string;
|
|
2391
|
+
agentId: string;
|
|
2392
|
+
requestId: string;
|
|
2393
|
+
name: string;
|
|
2394
|
+
scope: CredentialScope;
|
|
2395
|
+
/** Who provided it. Channel-native id (a Slack user). */
|
|
2396
|
+
actor?: string;
|
|
2397
|
+
/** Set when the key was stored but could not be added to the soul. */
|
|
2398
|
+
grantError?: string;
|
|
2072
2399
|
} | {
|
|
2073
2400
|
type: 'session.completed';
|
|
2074
2401
|
sessionId: string;
|
|
@@ -2549,6 +2876,15 @@ export interface SkillDescriptor {
|
|
|
2549
2876
|
* else, per turn and in memory.
|
|
2550
2877
|
*/
|
|
2551
2878
|
export interface Skill extends SkillDescriptor {
|
|
2879
|
+
/**
|
|
2880
|
+
* Ships with Stratus and is enabled for every agent, whatever its soul's
|
|
2881
|
+
* `skills:` says. Reserved for the system's own documentation (the
|
|
2882
|
+
* `stratus` skill; `SkillRegistry.register` refuses it on any other id): a skill from anywhere else changing how an agent
|
|
2883
|
+
* behaves is opted into, but an agent that cannot read how the system it
|
|
2884
|
+
* runs in works guesses instead, the way one suggested `passEnv` for a
|
|
2885
|
+
* secret after reading the install around it.
|
|
2886
|
+
*/
|
|
2887
|
+
builtin?: boolean;
|
|
2552
2888
|
/**
|
|
2553
2889
|
* Toolset globs this skill expects (`browser.*`, `fs.read`). Advisory: a
|
|
2554
2890
|
* skill is prose and degrades, so an agent enabling one without the tools
|
|
@@ -2633,6 +2969,12 @@ export declare class SkillRegistry {
|
|
|
2633
2969
|
* A read already underway keeps the promise it holds either way. `next`
|
|
2634
2970
|
* is consumed — emptied, not shared — so nothing can go on mutating this
|
|
2635
2971
|
* registry through it.
|
|
2972
|
+
*
|
|
2973
|
+
* A built-in skill serving now is never dropped by the swap: a `next`
|
|
2974
|
+
* without one (its file unreadable mid-reload) keeps the one serving,
|
|
2975
|
+
* and a bare alias `next` gave its id yields to it. A reload is
|
|
2976
|
+
* all-or-nothing for what it read; it must not take away from every
|
|
2977
|
+
* agent the one skill no soul can opt back into.
|
|
2636
2978
|
*/
|
|
2637
2979
|
replaceWith(next: SkillRegistry): void;
|
|
2638
2980
|
describe(): SkillDescriptor[];
|
|
@@ -2644,6 +2986,12 @@ export declare class SkillRegistry {
|
|
|
2644
2986
|
read(id: string): Promise<string>;
|
|
2645
2987
|
}
|
|
2646
2988
|
export declare const SKILL_READ_TOOL_NAME = "skill.read";
|
|
2989
|
+
/**
|
|
2990
|
+
* The built-in skill's id: how Stratus works, enabled for every agent.
|
|
2991
|
+
* Reserved, so an operator directory or a plugin cannot put a different
|
|
2992
|
+
* procedure under the name every agent is pointed at.
|
|
2993
|
+
*/
|
|
2994
|
+
export declare const STRATUS_SKILL_ID = "stratus";
|
|
2647
2995
|
export interface SkillReadToolOptions {
|
|
2648
2996
|
/**
|
|
2649
2997
|
* Which `skills:` entries the session's agent is held to. Defaults to the
|
|
@@ -2727,6 +3075,43 @@ export declare const renderMemorySection: (memory: readonly MemoryEntry[] | Memo
|
|
|
2727
3075
|
* here may ever include a body.
|
|
2728
3076
|
*/
|
|
2729
3077
|
export declare const renderSkillsSection: (skills: readonly SkillDescriptor[] | undefined) => string | undefined;
|
|
3078
|
+
/**
|
|
3079
|
+
* The language agents write in when nothing says otherwise: American
|
|
3080
|
+
* English. A soul's `language:` and the config file's `language` override
|
|
3081
|
+
* it; see `resolveRuntimeConfig`, which decides between those two.
|
|
3082
|
+
*/
|
|
3083
|
+
export declare const DEFAULT_LANGUAGE = "en-US";
|
|
3084
|
+
/** Whether a `language` setting is a BCP 47 tag; see `canonicalLanguageTag`. */
|
|
3085
|
+
export declare const isLanguageTag: (value: unknown) => value is string;
|
|
3086
|
+
/**
|
|
3087
|
+
* How to reply, told to every agent before its persona.
|
|
3088
|
+
*
|
|
3089
|
+
* Models default to the long, headed, bulleted answer, and an agent is read
|
|
3090
|
+
* mostly in chat, often on a phone, where that is the wrong shape. Souls
|
|
3091
|
+
* were each growing their own copy of these rules, so they live here once.
|
|
3092
|
+
* Short must not turn into incomplete, so the same section says a requested
|
|
3093
|
+
* deliverable arrives whole and a blocker is always said. A long one goes
|
|
3094
|
+
* where the reader can open it: an agent on Slack that answers with a path
|
|
3095
|
+
* on the daemon's disk has handed over nothing, so when sharing is not
|
|
3096
|
+
* possible the content itself goes in the reply.
|
|
3097
|
+
*
|
|
3098
|
+
* The accuracy rules are here for the same reason, each after an incident:
|
|
3099
|
+
* an agent with no Slack tool insisted it had no Slack connection, and
|
|
3100
|
+
* agents reported work as done that had only been drafted. The language
|
|
3101
|
+
* rule is here because the default persona is not: a custom soul replaces
|
|
3102
|
+
* it, and an agent wrote "favour" and "licence" into American website copy
|
|
3103
|
+
* because nothing shared said which English. A pasted British paragraph is
|
|
3104
|
+
* not a setting, so the rule says a task or a client's style guide decides
|
|
3105
|
+
* one deliverable and nothing is inferred from source text.
|
|
3106
|
+
*
|
|
3107
|
+
* The last sentence lets the soul and the person override length, format,
|
|
3108
|
+
* and language, and deliberately nothing else: a style preference never
|
|
3109
|
+
* changes what an agent may do, and "be brief" must not read as "skip the
|
|
3110
|
+
* blocker". Written without em dashes on purpose: a model imitates the
|
|
3111
|
+
* prose it is prompted with, and a rule against them set in them undoes
|
|
3112
|
+
* itself.
|
|
3113
|
+
*/
|
|
3114
|
+
export declare const renderReplySection: (language: string | undefined) => string;
|
|
2730
3115
|
/**
|
|
2731
3116
|
* Where the conversation is happening, for a session a channel started.
|
|
2732
3117
|
*
|
|
@@ -2734,11 +3119,39 @@ export declare const renderSkillsSection: (skills: readonly SkillDescriptor[] |
|
|
|
2734
3119
|
* dispatches (`slack` today). Without this the agent could not tell: an
|
|
2735
3120
|
* agent with no Slack tool, asked about an attachment in a Slack DM, told
|
|
2736
3121
|
* the person it had "no Slack connection at all" and argued the point.
|
|
2737
|
-
*
|
|
2738
|
-
*
|
|
3122
|
+
* The channel is read from the session, so sessions opened before this
|
|
3123
|
+
* existed get it too; the room (`ConversationContext`) comes per turn from
|
|
3124
|
+
* the runtime, falling back to what the session's first turn recorded, so
|
|
3125
|
+
* an agent in a Slack DM no longer tells the person they are "talking on
|
|
3126
|
+
* the terminal". The name must look like a channel
|
|
2739
3127
|
* id, since it is interpolated into the prompt.
|
|
3128
|
+
*
|
|
3129
|
+
* What it says about attachments is what the adapter writes into the
|
|
3130
|
+
* message, not a promise that they arrive: it used to say that what people
|
|
3131
|
+
* attach "reaches you", which read as though every file's contents did,
|
|
3132
|
+
* when most reach the turn as a name only. The Slack adapter marks each
|
|
3133
|
+
* file as read, shown, or unread with its reason; the section sends the
|
|
3134
|
+
* model to that note and nowhere else.
|
|
3135
|
+
*/
|
|
3136
|
+
export declare const renderChannelSection: (session: Pick<Session, "metadata">, conversation?: ConversationContext) => string | undefined;
|
|
3137
|
+
/**
|
|
3138
|
+
* How this agent is run, for a host that said (see
|
|
3139
|
+
* {@link AgentRuntimeContext}).
|
|
3140
|
+
*
|
|
3141
|
+
* It says the soul is already in the prompt as firmly as it says where the
|
|
3142
|
+
* file is. An agent told only a path treats it as something to go and read,
|
|
3143
|
+
* and it usually cannot: the soul sits beside its state directory, outside
|
|
3144
|
+
* any root an operator hands `tool-fs`, and it has to stay there, since
|
|
3145
|
+
* the soul is what grants the agent its tools and credentials.
|
|
3146
|
+
*
|
|
3147
|
+
* Credentials get the same treatment, and for the same reason: an agent
|
|
3148
|
+
* whose operator had stored a shared key went looking through its files and
|
|
3149
|
+
* its environment for it. A named credential only ever reaches a plugin tool
|
|
3150
|
+
* that declared it, so the section names what the soul grants and says there
|
|
3151
|
+
* is nothing to find. Names only; a value never comes near a prompt.
|
|
3152
|
+
* Written without em dashes for the reason the reply section is.
|
|
2740
3153
|
*/
|
|
2741
|
-
export declare const
|
|
3154
|
+
export declare const renderRuntimeSection: (runtime: AgentRuntimeContext | undefined, agent: Pick<AgentDefinition, "credentials">) => string | undefined;
|
|
2742
3155
|
export interface SystemPromptOptions {
|
|
2743
3156
|
/** Host-level preamble, rendered before the agent's own persona. */
|
|
2744
3157
|
preamble?: string;
|
|
@@ -2749,12 +3162,12 @@ export interface SystemPromptOptions {
|
|
|
2749
3162
|
* Which part of what an agent is told a section is.
|
|
2750
3163
|
*
|
|
2751
3164
|
* The distinction a caller actually needs is stable versus volatile:
|
|
2752
|
-
* `preamble`, `replies`, `persona`, `channel`, and `skills` are byte-identical across every turn of
|
|
3165
|
+
* `preamble`, `replies`, `persona`, `runtime`, `channel`, and `skills` are byte-identical across every turn of
|
|
2753
3166
|
* an agent's life, while `memory` is rewritten whenever the agent remembers
|
|
2754
3167
|
* anything. A provider that caches its request prefix has to place those two
|
|
2755
3168
|
* groups differently, and it cannot tell them apart from rendered strings.
|
|
2756
3169
|
*/
|
|
2757
|
-
export type SystemPromptSectionKind = 'preamble' | 'replies' | 'persona' | 'channel' | 'memory' | 'skills';
|
|
3170
|
+
export type SystemPromptSectionKind = 'preamble' | 'replies' | 'persona' | 'runtime' | 'channel' | 'memory' | 'skills';
|
|
2758
3171
|
export interface SystemPromptSection {
|
|
2759
3172
|
kind: SystemPromptSectionKind;
|
|
2760
3173
|
text: string;
|
|
@@ -2773,11 +3186,11 @@ export interface SystemPromptSection {
|
|
|
2773
3186
|
* The order is the order. This is the one rule about what an agent is told;
|
|
2774
3187
|
* `renderSystemPromptSections` is the same rule with the labels dropped.
|
|
2775
3188
|
*/
|
|
2776
|
-
export declare const renderSystemPromptParts: (request: Pick<ProviderRequest, "session" | "memory" | "skills">, options?: SystemPromptOptions) => SystemPromptSection[];
|
|
3189
|
+
export declare const renderSystemPromptParts: (request: Pick<ProviderRequest, "session" | "memory" | "skills" | "runtime">, options?: SystemPromptOptions) => SystemPromptSection[];
|
|
2777
3190
|
/** The sections as plain strings, in the same order. */
|
|
2778
|
-
export declare const renderSystemPromptSections: (request: Pick<ProviderRequest, "session" | "memory" | "skills">, options?: SystemPromptOptions) => string[];
|
|
3191
|
+
export declare const renderSystemPromptSections: (request: Pick<ProviderRequest, "session" | "memory" | "skills" | "runtime">, options?: SystemPromptOptions) => string[];
|
|
2779
3192
|
/** The sections joined the way single-string providers send them. */
|
|
2780
|
-
export declare const renderSystemPrompt: (request: Pick<ProviderRequest, "session" | "memory" | "skills">, options?: SystemPromptOptions) => string | undefined;
|
|
3193
|
+
export declare const renderSystemPrompt: (request: Pick<ProviderRequest, "session" | "memory" | "skills" | "runtime">, options?: SystemPromptOptions) => string | undefined;
|
|
2781
3194
|
/**
|
|
2782
3195
|
* The two producer channels, collected for one call: the tool's standing
|
|
2783
3196
|
* `outputTrust` declaration and whatever the tool marks through the
|
|
@@ -2889,7 +3302,31 @@ export interface RunInput {
|
|
|
2889
3302
|
* attachments instead, as an overheard one does.
|
|
2890
3303
|
*/
|
|
2891
3304
|
addressed?: boolean;
|
|
3305
|
+
/**
|
|
3306
|
+
* The caller's name for this dispatch, stored on the user message
|
|
3307
|
+
* (`Message.idempotencyKey`) in the same write as the message itself.
|
|
3308
|
+
* The runner only records it; refusing a repeat is the host's job — see
|
|
3309
|
+
* `workItemState` and the gateway's `DispatchInput.idempotencyKey`.
|
|
3310
|
+
*/
|
|
3311
|
+
idempotencyKey?: string;
|
|
3312
|
+
/**
|
|
3313
|
+
* Whether this turn may run on a provider that hosts its own tool loop,
|
|
3314
|
+
* stored on the user message (`Message.hostedLoop`). The host knows what
|
|
3315
|
+
* it resolved for the turn; the runner only records it.
|
|
3316
|
+
*/
|
|
3317
|
+
hostedLoop?: boolean;
|
|
2892
3318
|
metadata?: JsonObject;
|
|
3319
|
+
/**
|
|
3320
|
+
* What the host can say about how this agent is run, rendered as the
|
|
3321
|
+
* `runtime` section; see {@link AgentRuntimeContext}. Omitted, the
|
|
3322
|
+
* section is not rendered.
|
|
3323
|
+
*
|
|
3324
|
+
* Per run rather than a runner option, because the answer belongs to the
|
|
3325
|
+
* snapshot the host dispatched with: a daemon that re-reads the roster
|
|
3326
|
+
* between two awaits would otherwise tell an agent running on one soul
|
|
3327
|
+
* file that another file is its soul.
|
|
3328
|
+
*/
|
|
3329
|
+
runtime?: AgentRuntimeContext;
|
|
2893
3330
|
/** Aborting fails the turn cleanly; see RunAbortedError. */
|
|
2894
3331
|
signal?: AbortSignal;
|
|
2895
3332
|
}
|
|
@@ -2900,6 +3337,10 @@ export interface ResumeInput {
|
|
|
2900
3337
|
images?: ImageAttachment[];
|
|
2901
3338
|
/** See `RunInput.addressed`. */
|
|
2902
3339
|
addressed?: boolean;
|
|
3340
|
+
/** See `RunInput.idempotencyKey`. */
|
|
3341
|
+
idempotencyKey?: string;
|
|
3342
|
+
/** See `RunInput.hostedLoop`. */
|
|
3343
|
+
hostedLoop?: boolean;
|
|
2903
3344
|
/**
|
|
2904
3345
|
* This turn's metadata — read for the sender's trust
|
|
2905
3346
|
* (`SENDER_TRUST_METADATA_KEY`) and not merged into the session's. The
|
|
@@ -2908,6 +3349,8 @@ export interface ResumeInput {
|
|
|
2908
3349
|
* trust.
|
|
2909
3350
|
*/
|
|
2910
3351
|
metadata?: JsonObject;
|
|
3352
|
+
/** See `RunInput.runtime`. */
|
|
3353
|
+
runtime?: AgentRuntimeContext;
|
|
2911
3354
|
/** Aborting fails the turn cleanly; see RunAbortedError. */
|
|
2912
3355
|
signal?: AbortSignal;
|
|
2913
3356
|
}
|
|
@@ -3104,6 +3547,30 @@ export declare class AgentRunner {
|
|
|
3104
3547
|
*/
|
|
3105
3548
|
private labelLegacySession;
|
|
3106
3549
|
resume(input: ResumeInput): Promise<Session>;
|
|
3550
|
+
/**
|
|
3551
|
+
* Picks up a turn whose process died while it was `running`: its user
|
|
3552
|
+
* message is durable and its reply is not. Nothing is appended — the turn
|
|
3553
|
+
* already has its message — so this is `resume` without the input.
|
|
3554
|
+
* Dangling tool calls are closed as interrupted, telling the model they
|
|
3555
|
+
* may not have run rather than running them again, and the loop goes back
|
|
3556
|
+
* to the provider with the transcript as it stands.
|
|
3557
|
+
*
|
|
3558
|
+
* A transcript that already ends in the reply (the response was saved,
|
|
3559
|
+
* and the process died before the save that marks the turn completed) is
|
|
3560
|
+
* completed as it stands, with no provider call: asking again would send
|
|
3561
|
+
* a second answer to one message.
|
|
3562
|
+
*
|
|
3563
|
+
* Marks the turn's message `continuedAfterCrash` in its first save, so a
|
|
3564
|
+
* host can refuse to do this twice. Resolves `undefined` when the session
|
|
3565
|
+
* is missing or no longer `running`. Never for a turn parked on a human:
|
|
3566
|
+
* `recoverPendingApproval` re-enters the parked call, which reconciling
|
|
3567
|
+
* here would close.
|
|
3568
|
+
*/
|
|
3569
|
+
continueTurn(sessionId: string, options?: {
|
|
3570
|
+
/** See `RunInput.runtime`. */
|
|
3571
|
+
runtime?: AgentRuntimeContext;
|
|
3572
|
+
signal?: AbortSignal;
|
|
3573
|
+
}): Promise<Session | undefined>;
|
|
3107
3574
|
/**
|
|
3108
3575
|
* A message reaching the session with no turn run on it: something said
|
|
3109
3576
|
* in a conversation the agent is in, to somebody else. Appended and
|
|
@@ -3122,11 +3589,18 @@ export declare class AgentRunner {
|
|
|
3122
3589
|
*/
|
|
3123
3590
|
observe(input: ObserveInput): Promise<Session>;
|
|
3124
3591
|
/**
|
|
3125
|
-
*
|
|
3126
|
-
*
|
|
3127
|
-
*
|
|
3128
|
-
*
|
|
3129
|
-
*
|
|
3592
|
+
* Adds a synthetic failed result for every tool call that has none — the
|
|
3593
|
+
* durable trace of a turn interrupted between the call's save and its
|
|
3594
|
+
* result's. The model sees an honest record ("interrupted, never ran to
|
|
3595
|
+
* completion") instead of a wire-format violation, and a resume can
|
|
3596
|
+
* decide to retry rather than assume the side effect landed.
|
|
3597
|
+
*
|
|
3598
|
+
* Placed where an uninterrupted turn would have written it: after the
|
|
3599
|
+
* whole response the call came in — every assistant message that one
|
|
3600
|
+
* save wrote — and after any results already recorded for it. Directly
|
|
3601
|
+
* after the call would split a response whose text followed its call, and
|
|
3602
|
+
* a provider replaying the response's raw turn (which already holds that
|
|
3603
|
+
* text) would then send the text a second time.
|
|
3130
3604
|
*/
|
|
3131
3605
|
private reconcileInterruptedToolCalls;
|
|
3132
3606
|
/**
|
|
@@ -3163,11 +3637,14 @@ export declare class AgentRunner {
|
|
|
3163
3637
|
* the allowlist actually granted (canonical before alias) — the id the
|
|
3164
3638
|
* model is told is the id `skill.read` will accept.
|
|
3165
3639
|
*
|
|
3166
|
-
*
|
|
3167
|
-
*
|
|
3640
|
+
* A built-in skill is in it for every agent; otherwise it is empty for
|
|
3641
|
+
* an agent whose soul has no `skills:` key, which is what keys both
|
|
3642
|
+
* `skill.read` gates: no skills, no reader.
|
|
3168
3643
|
*/
|
|
3169
3644
|
private enabledSkillsFor;
|
|
3170
3645
|
private executeTurns;
|
|
3646
|
+
/** See `completeAnsweredTurn`; the loop's end and `continueTurn`'s. */
|
|
3647
|
+
private completeTurn;
|
|
3171
3648
|
/**
|
|
3172
3649
|
* Finishes a turn that was parked on a human when the process died.
|
|
3173
3650
|
*
|
|
@@ -3188,6 +3665,8 @@ export declare class AgentRunner {
|
|
|
3188
3665
|
* about at all, so they still face the policy normally.
|
|
3189
3666
|
*/
|
|
3190
3667
|
denyPending?: boolean;
|
|
3668
|
+
/** See `RunInput.runtime`. */
|
|
3669
|
+
runtime?: AgentRuntimeContext;
|
|
3191
3670
|
signal?: AbortSignal;
|
|
3192
3671
|
}): Promise<Session | undefined>;
|
|
3193
3672
|
/**
|