@rowan-agent/agent 0.9.11 → 0.9.13
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 +165 -153
- package/dist/index.js +138 -28
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -55,6 +55,159 @@ type ToolResult = {
|
|
|
55
55
|
error?: string;
|
|
56
56
|
};
|
|
57
57
|
|
|
58
|
+
interface BeforePhaseEvent {
|
|
59
|
+
type: "before_phase";
|
|
60
|
+
phaseId: string;
|
|
61
|
+
input: PhaseContext;
|
|
62
|
+
}
|
|
63
|
+
interface AfterPhaseEvent {
|
|
64
|
+
type: "after_phase";
|
|
65
|
+
phaseId: string;
|
|
66
|
+
output: PhaseOutput;
|
|
67
|
+
}
|
|
68
|
+
interface BeforePromptEvent {
|
|
69
|
+
type: "before_prompt";
|
|
70
|
+
phaseId: string;
|
|
71
|
+
input: PhaseContext;
|
|
72
|
+
}
|
|
73
|
+
interface BeforeToolCallEvent {
|
|
74
|
+
type: "before_tool_call";
|
|
75
|
+
tool: Tool$1;
|
|
76
|
+
args: unknown;
|
|
77
|
+
}
|
|
78
|
+
interface AfterToolCallEvent {
|
|
79
|
+
type: "after_tool_call";
|
|
80
|
+
tool: Tool$1;
|
|
81
|
+
result: ToolResult;
|
|
82
|
+
}
|
|
83
|
+
type HookEvent = BeforePhaseEvent | AfterPhaseEvent | BeforePromptEvent | BeforeToolCallEvent | AfterToolCallEvent;
|
|
84
|
+
interface BeforePhaseResult {
|
|
85
|
+
abort?: Outcome$1;
|
|
86
|
+
skip?: {
|
|
87
|
+
route: string;
|
|
88
|
+
message: string;
|
|
89
|
+
};
|
|
90
|
+
input?: PhaseContext;
|
|
91
|
+
}
|
|
92
|
+
interface AfterPhaseResult {
|
|
93
|
+
abort?: Outcome$1;
|
|
94
|
+
retry?: PhaseContext;
|
|
95
|
+
output?: PhaseOutput;
|
|
96
|
+
}
|
|
97
|
+
interface BeforePromptResult {
|
|
98
|
+
input?: PhaseContext;
|
|
99
|
+
}
|
|
100
|
+
interface BeforeToolCallResult {
|
|
101
|
+
allow: boolean;
|
|
102
|
+
reason?: string;
|
|
103
|
+
}
|
|
104
|
+
interface AfterToolCallResult {
|
|
105
|
+
result?: ToolResult;
|
|
106
|
+
}
|
|
107
|
+
interface HookResultMap {
|
|
108
|
+
before_phase: BeforePhaseResult | undefined;
|
|
109
|
+
after_phase: AfterPhaseResult | undefined;
|
|
110
|
+
before_prompt: BeforePromptResult | undefined;
|
|
111
|
+
before_tool_call: BeforeToolCallResult | undefined;
|
|
112
|
+
after_tool_call: AfterToolCallResult | undefined;
|
|
113
|
+
}
|
|
114
|
+
type HookEventType = HookEvent["type"];
|
|
115
|
+
type HookHandler<K extends HookEventType> = (event: Extract<HookEvent, {
|
|
116
|
+
type: K;
|
|
117
|
+
}>) => HookResultMap[K] | Promise<HookResultMap[K]> | void | Promise<void>;
|
|
118
|
+
declare class HooksManager {
|
|
119
|
+
private handlers;
|
|
120
|
+
on<K extends HookEventType>(eventType: K, handler: HookHandler<K>): void;
|
|
121
|
+
off<K extends HookEventType>(eventType: K, handler: HookHandler<K>): void;
|
|
122
|
+
emit<K extends HookEventType>(eventType: K, event: Extract<HookEvent, {
|
|
123
|
+
type: K;
|
|
124
|
+
}>): Promise<void>;
|
|
125
|
+
emitFirst<K extends HookEventType>(eventType: K, event: Extract<HookEvent, {
|
|
126
|
+
type: K;
|
|
127
|
+
}>): Promise<HookResultMap[K] | undefined>;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
type LoopMetrics = {
|
|
131
|
+
/** Number of phase iterations executed. */
|
|
132
|
+
iterations: number;
|
|
133
|
+
/** Phase transition history. */
|
|
134
|
+
phaseTransitions: Array<{
|
|
135
|
+
from: string;
|
|
136
|
+
to: string;
|
|
137
|
+
ts: string;
|
|
138
|
+
}>;
|
|
139
|
+
/** Number of times compaction was triggered. */
|
|
140
|
+
compactionCount: number;
|
|
141
|
+
/** Number of retry attempts due to transient errors. */
|
|
142
|
+
retryCount: number;
|
|
143
|
+
/** Loop start timestamp. */
|
|
144
|
+
startedAt: string;
|
|
145
|
+
/** Loop start time as epoch ms (for duration calculation). */
|
|
146
|
+
startedAtMs: number;
|
|
147
|
+
/** Loop end timestamp (set on completion). */
|
|
148
|
+
endedAt?: string;
|
|
149
|
+
/** Total wall-clock duration in ms. */
|
|
150
|
+
durationMs?: number;
|
|
151
|
+
};
|
|
152
|
+
type ExecutionState = {
|
|
153
|
+
currentPhase: string;
|
|
154
|
+
attempt: number;
|
|
155
|
+
metrics: LoopMetrics;
|
|
156
|
+
status: "idle" | "running" | "suspended" | "completed" | "aborted" | "failed";
|
|
157
|
+
continuation?: ExecutionContinuationState;
|
|
158
|
+
phaseInteractions?: PhaseInteractionState;
|
|
159
|
+
};
|
|
160
|
+
type ExecutionContinuationState = {
|
|
161
|
+
isContinuing: boolean;
|
|
162
|
+
previousPayload?: unknown;
|
|
163
|
+
previousResults: Array<{
|
|
164
|
+
name: string;
|
|
165
|
+
output?: unknown;
|
|
166
|
+
}>;
|
|
167
|
+
pendingInstruction?: string;
|
|
168
|
+
previousPhaseMessageId?: string;
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
type PhaseInteractionKind = "user_input" | "permission" | "elicitation" | "confirmation";
|
|
172
|
+
type PhaseInteractionStatus = "pending" | "answered" | "denied" | "cancelled" | "expired";
|
|
173
|
+
type PhaseInteraction = Readonly<{
|
|
174
|
+
id: string;
|
|
175
|
+
phase: string;
|
|
176
|
+
kind: PhaseInteractionKind;
|
|
177
|
+
prompt: string;
|
|
178
|
+
payload?: JsonValue;
|
|
179
|
+
createdAt: string;
|
|
180
|
+
status: PhaseInteractionStatus;
|
|
181
|
+
}>;
|
|
182
|
+
type PhaseInteractionState = Readonly<{
|
|
183
|
+
requests: readonly PhaseInteraction[];
|
|
184
|
+
answers: Readonly<Record<string, JsonValue>>;
|
|
185
|
+
checkpoint?: JsonValue;
|
|
186
|
+
}>;
|
|
187
|
+
type PhaseInteractionDriver = Readonly<{
|
|
188
|
+
signal: AbortSignal;
|
|
189
|
+
request(input: Readonly<{
|
|
190
|
+
id?: string;
|
|
191
|
+
kind: PhaseInteractionKind;
|
|
192
|
+
prompt: string;
|
|
193
|
+
payload?: JsonValue;
|
|
194
|
+
}>): PhaseInteraction;
|
|
195
|
+
pending(): readonly PhaseInteraction[];
|
|
196
|
+
answers(): ReadonlyMap<string, JsonValue>;
|
|
197
|
+
suspend(input?: Readonly<{
|
|
198
|
+
checkpoint?: JsonValue;
|
|
199
|
+
}>): never;
|
|
200
|
+
}>;
|
|
201
|
+
declare class PhaseInteractionCancelledError extends Error {
|
|
202
|
+
readonly code: "phase_interaction_cancelled";
|
|
203
|
+
constructor();
|
|
204
|
+
}
|
|
205
|
+
declare class PhaseInteractionBoundary extends Error {
|
|
206
|
+
readonly state: ExecutionState;
|
|
207
|
+
readonly interactions: readonly PhaseInteraction[];
|
|
208
|
+
constructor(state: ExecutionState, interactions: readonly PhaseInteraction[]);
|
|
209
|
+
}
|
|
210
|
+
|
|
58
211
|
/** Durable and transient DTOs owned by the event-driven Agent Runtime. */
|
|
59
212
|
|
|
60
213
|
declare const opaqueIdBrand: unique symbol;
|
|
@@ -387,159 +540,6 @@ type PhaseStatusEvent = Readonly<{
|
|
|
387
540
|
}>;
|
|
388
541
|
type RunEvent = DurableRunEvent | MessageDelta | ThinkingDelta | ToolProgress | PhaseStatusEvent;
|
|
389
542
|
|
|
390
|
-
interface BeforePhaseEvent {
|
|
391
|
-
type: "before_phase";
|
|
392
|
-
phaseId: string;
|
|
393
|
-
input: PhaseContext;
|
|
394
|
-
}
|
|
395
|
-
interface AfterPhaseEvent {
|
|
396
|
-
type: "after_phase";
|
|
397
|
-
phaseId: string;
|
|
398
|
-
output: PhaseOutput;
|
|
399
|
-
}
|
|
400
|
-
interface BeforePromptEvent {
|
|
401
|
-
type: "before_prompt";
|
|
402
|
-
phaseId: string;
|
|
403
|
-
input: PhaseContext;
|
|
404
|
-
}
|
|
405
|
-
interface BeforeToolCallEvent {
|
|
406
|
-
type: "before_tool_call";
|
|
407
|
-
tool: Tool$1;
|
|
408
|
-
args: unknown;
|
|
409
|
-
}
|
|
410
|
-
interface AfterToolCallEvent {
|
|
411
|
-
type: "after_tool_call";
|
|
412
|
-
tool: Tool$1;
|
|
413
|
-
result: ToolResult;
|
|
414
|
-
}
|
|
415
|
-
type HookEvent = BeforePhaseEvent | AfterPhaseEvent | BeforePromptEvent | BeforeToolCallEvent | AfterToolCallEvent;
|
|
416
|
-
interface BeforePhaseResult {
|
|
417
|
-
abort?: Outcome$1;
|
|
418
|
-
skip?: {
|
|
419
|
-
route: string;
|
|
420
|
-
message: string;
|
|
421
|
-
};
|
|
422
|
-
input?: PhaseContext;
|
|
423
|
-
}
|
|
424
|
-
interface AfterPhaseResult {
|
|
425
|
-
abort?: Outcome$1;
|
|
426
|
-
retry?: PhaseContext;
|
|
427
|
-
output?: PhaseOutput;
|
|
428
|
-
}
|
|
429
|
-
interface BeforePromptResult {
|
|
430
|
-
input?: PhaseContext;
|
|
431
|
-
}
|
|
432
|
-
interface BeforeToolCallResult {
|
|
433
|
-
allow: boolean;
|
|
434
|
-
reason?: string;
|
|
435
|
-
}
|
|
436
|
-
interface AfterToolCallResult {
|
|
437
|
-
result?: ToolResult;
|
|
438
|
-
}
|
|
439
|
-
interface HookResultMap {
|
|
440
|
-
before_phase: BeforePhaseResult | undefined;
|
|
441
|
-
after_phase: AfterPhaseResult | undefined;
|
|
442
|
-
before_prompt: BeforePromptResult | undefined;
|
|
443
|
-
before_tool_call: BeforeToolCallResult | undefined;
|
|
444
|
-
after_tool_call: AfterToolCallResult | undefined;
|
|
445
|
-
}
|
|
446
|
-
type HookEventType = HookEvent["type"];
|
|
447
|
-
type HookHandler<K extends HookEventType> = (event: Extract<HookEvent, {
|
|
448
|
-
type: K;
|
|
449
|
-
}>) => HookResultMap[K] | Promise<HookResultMap[K]> | void | Promise<void>;
|
|
450
|
-
declare class HooksManager {
|
|
451
|
-
private handlers;
|
|
452
|
-
on<K extends HookEventType>(eventType: K, handler: HookHandler<K>): void;
|
|
453
|
-
off<K extends HookEventType>(eventType: K, handler: HookHandler<K>): void;
|
|
454
|
-
emit<K extends HookEventType>(eventType: K, event: Extract<HookEvent, {
|
|
455
|
-
type: K;
|
|
456
|
-
}>): Promise<void>;
|
|
457
|
-
emitFirst<K extends HookEventType>(eventType: K, event: Extract<HookEvent, {
|
|
458
|
-
type: K;
|
|
459
|
-
}>): Promise<HookResultMap[K] | undefined>;
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
type LoopMetrics = {
|
|
463
|
-
/** Number of phase iterations executed. */
|
|
464
|
-
iterations: number;
|
|
465
|
-
/** Phase transition history. */
|
|
466
|
-
phaseTransitions: Array<{
|
|
467
|
-
from: string;
|
|
468
|
-
to: string;
|
|
469
|
-
ts: string;
|
|
470
|
-
}>;
|
|
471
|
-
/** Number of times compaction was triggered. */
|
|
472
|
-
compactionCount: number;
|
|
473
|
-
/** Number of retry attempts due to transient errors. */
|
|
474
|
-
retryCount: number;
|
|
475
|
-
/** Loop start timestamp. */
|
|
476
|
-
startedAt: string;
|
|
477
|
-
/** Loop start time as epoch ms (for duration calculation). */
|
|
478
|
-
startedAtMs: number;
|
|
479
|
-
/** Loop end timestamp (set on completion). */
|
|
480
|
-
endedAt?: string;
|
|
481
|
-
/** Total wall-clock duration in ms. */
|
|
482
|
-
durationMs?: number;
|
|
483
|
-
};
|
|
484
|
-
type ExecutionState = {
|
|
485
|
-
currentPhase: string;
|
|
486
|
-
attempt: number;
|
|
487
|
-
metrics: LoopMetrics;
|
|
488
|
-
status: "idle" | "running" | "suspended" | "completed" | "aborted" | "failed";
|
|
489
|
-
continuation?: ExecutionContinuationState;
|
|
490
|
-
phaseInteractions?: PhaseInteractionState;
|
|
491
|
-
};
|
|
492
|
-
type ExecutionContinuationState = {
|
|
493
|
-
isContinuing: boolean;
|
|
494
|
-
previousPayload?: unknown;
|
|
495
|
-
previousResults: Array<{
|
|
496
|
-
name: string;
|
|
497
|
-
output?: unknown;
|
|
498
|
-
}>;
|
|
499
|
-
pendingInstruction?: string;
|
|
500
|
-
previousPhaseMessageId?: string;
|
|
501
|
-
};
|
|
502
|
-
|
|
503
|
-
type PhaseInteractionKind = "user_input" | "permission" | "elicitation" | "confirmation";
|
|
504
|
-
type PhaseInteractionStatus = "pending" | "answered" | "denied" | "cancelled" | "expired";
|
|
505
|
-
type PhaseInteraction = Readonly<{
|
|
506
|
-
id: string;
|
|
507
|
-
phase: string;
|
|
508
|
-
kind: PhaseInteractionKind;
|
|
509
|
-
prompt: string;
|
|
510
|
-
payload?: JsonValue;
|
|
511
|
-
createdAt: string;
|
|
512
|
-
status: PhaseInteractionStatus;
|
|
513
|
-
}>;
|
|
514
|
-
type PhaseInteractionState = Readonly<{
|
|
515
|
-
requests: readonly PhaseInteraction[];
|
|
516
|
-
answers: Readonly<Record<string, JsonValue>>;
|
|
517
|
-
checkpoint?: JsonValue;
|
|
518
|
-
}>;
|
|
519
|
-
type PhaseInteractionDriver = Readonly<{
|
|
520
|
-
signal: AbortSignal;
|
|
521
|
-
request(input: Readonly<{
|
|
522
|
-
id?: string;
|
|
523
|
-
kind: PhaseInteractionKind;
|
|
524
|
-
prompt: string;
|
|
525
|
-
payload?: JsonValue;
|
|
526
|
-
}>): PhaseInteraction;
|
|
527
|
-
pending(): readonly PhaseInteraction[];
|
|
528
|
-
answers(): ReadonlyMap<string, JsonValue>;
|
|
529
|
-
suspend(input?: Readonly<{
|
|
530
|
-
checkpoint?: JsonValue;
|
|
531
|
-
}>): never;
|
|
532
|
-
}>;
|
|
533
|
-
declare class PhaseInteractionCancelledError extends Error {
|
|
534
|
-
readonly code: "phase_interaction_cancelled";
|
|
535
|
-
constructor();
|
|
536
|
-
}
|
|
537
|
-
declare class PhaseInteractionBoundary extends Error {
|
|
538
|
-
readonly state: ExecutionState;
|
|
539
|
-
readonly interactions: readonly PhaseInteraction[];
|
|
540
|
-
constructor(state: ExecutionState, interactions: readonly PhaseInteraction[]);
|
|
541
|
-
}
|
|
542
|
-
|
|
543
543
|
type ModelInvokeOutput = {
|
|
544
544
|
text: string;
|
|
545
545
|
contentBlocks: ContentBlock[];
|
|
@@ -932,6 +932,8 @@ type PhaseExecutionIdentity = Readonly<{
|
|
|
932
932
|
agentId: string;
|
|
933
933
|
runId: string;
|
|
934
934
|
executionId: string;
|
|
935
|
+
/** Original durable Run input, before any Phase-local interpretation. */
|
|
936
|
+
input?: UserContent;
|
|
935
937
|
/** Opaque metadata captured at the durable boundary; Rowan does not decode it. */
|
|
936
938
|
agentMetadata?: Readonly<Record<string, unknown>>;
|
|
937
939
|
runMetadata?: Readonly<Record<string, unknown>>;
|
|
@@ -1378,6 +1380,8 @@ type AgentConfiguration = Readonly<{
|
|
|
1378
1380
|
}>;
|
|
1379
1381
|
resourceView: ResourceView;
|
|
1380
1382
|
contexts?: readonly ContextCandidate[];
|
|
1383
|
+
/** Trusted Host-owned Contexts rendered as durable user messages. */
|
|
1384
|
+
additionalContexts?: readonly ContextCandidate[];
|
|
1381
1385
|
cwd?: string;
|
|
1382
1386
|
maxAttempts?: number;
|
|
1383
1387
|
model: ModelConfig | ModelRef;
|
|
@@ -1394,6 +1398,7 @@ type ConfigurationSnapshot = Readonly<{
|
|
|
1394
1398
|
refs: Readonly<Record<ResourceKind, readonly ResourceRef[]>>;
|
|
1395
1399
|
}>;
|
|
1396
1400
|
contexts: readonly ContextCandidate[];
|
|
1401
|
+
additionalContexts: readonly ContextCandidate[];
|
|
1397
1402
|
resourceView: ResourceView;
|
|
1398
1403
|
cwd?: string;
|
|
1399
1404
|
maxAttempts?: number;
|
|
@@ -1502,6 +1507,8 @@ type AgentConfig = Readonly<{
|
|
|
1502
1507
|
identity: string;
|
|
1503
1508
|
definition: AgentDefinition;
|
|
1504
1509
|
resources: AgentResources;
|
|
1510
|
+
/** Host-owned Contexts are rendered as durable user messages for a Run. */
|
|
1511
|
+
additionalContexts?: readonly ContextCandidate[];
|
|
1505
1512
|
cwd?: string;
|
|
1506
1513
|
maxAttempts?: number;
|
|
1507
1514
|
beforeToolCall?: BeforeToolCall;
|
|
@@ -1766,6 +1773,7 @@ interface OwnedStore {
|
|
|
1766
1773
|
executionId?: ExecutionId;
|
|
1767
1774
|
messageId?: MessageId;
|
|
1768
1775
|
configToken?: ConfigToken;
|
|
1776
|
+
inputContext?: UserInput;
|
|
1769
1777
|
}): Promise<RunClaim>;
|
|
1770
1778
|
failQueuedRun(input: {
|
|
1771
1779
|
runId: RunId;
|
|
@@ -1938,6 +1946,7 @@ interface AgentRuntime$1 {
|
|
|
1938
1946
|
contextWindow?: number;
|
|
1939
1947
|
}): Promise<ContextStatus>;
|
|
1940
1948
|
compactContext(agentId: AgentId, options?: {
|
|
1949
|
+
input?: UserInput;
|
|
1941
1950
|
instructions?: string;
|
|
1942
1951
|
idempotencyKey?: string;
|
|
1943
1952
|
}): Promise<AgentRun>;
|
|
@@ -2029,6 +2038,7 @@ declare class AgentRuntime implements AgentRuntime$1 {
|
|
|
2029
2038
|
contextWindow?: number;
|
|
2030
2039
|
}): Promise<ContextStatus>;
|
|
2031
2040
|
compactContext(agentId: AgentId, options?: {
|
|
2041
|
+
input?: UserInput;
|
|
2032
2042
|
instructions?: string;
|
|
2033
2043
|
idempotencyKey?: string;
|
|
2034
2044
|
}): Promise<AgentRun>;
|
|
@@ -2178,6 +2188,7 @@ declare class InMemoryStore implements DurableStore {
|
|
|
2178
2188
|
executionId?: ExecutionId;
|
|
2179
2189
|
messageId?: MessageId;
|
|
2180
2190
|
configToken?: ConfigToken;
|
|
2191
|
+
inputContext?: UserInput;
|
|
2181
2192
|
}): RunClaim;
|
|
2182
2193
|
failQueuedRun(lease: OwnerLease, input: {
|
|
2183
2194
|
runId: RunId;
|
|
@@ -2361,6 +2372,7 @@ declare class SqliteStore implements DurableStore {
|
|
|
2361
2372
|
executionId?: ExecutionId;
|
|
2362
2373
|
messageId?: MessageId;
|
|
2363
2374
|
configToken?: ConfigToken;
|
|
2375
|
+
inputContext?: UserInput;
|
|
2364
2376
|
}): Promise<RunClaim>;
|
|
2365
2377
|
failQueuedRun(lease: OwnerLease, input: {
|
|
2366
2378
|
runId: RunId;
|
package/dist/index.js
CHANGED
|
@@ -46,6 +46,9 @@ function buildContextDescription(contexts) {
|
|
|
46
46
|
lines.push("</agent_context>");
|
|
47
47
|
return lines.join("\n");
|
|
48
48
|
}
|
|
49
|
+
function buildAdditionalContextMessage(contexts) {
|
|
50
|
+
return buildContextDescription(contexts);
|
|
51
|
+
}
|
|
49
52
|
function formatResourceOutput(resource) {
|
|
50
53
|
const parts = [`<${resource.type} name="${escapeXml(resource.name)}" location="${escapeXml(resource.location)}">`];
|
|
51
54
|
if (resource.baseDir) {
|
|
@@ -1751,7 +1754,7 @@ async function loadPhaseCode(codePath) {
|
|
|
1751
1754
|
throw new Error(`Phase code at "${codePath}" must export a default function or a run() function.`);
|
|
1752
1755
|
}
|
|
1753
1756
|
|
|
1754
|
-
// src/harness/phases/
|
|
1757
|
+
// src/harness/phases/core-phases.ts
|
|
1755
1758
|
var DEFAULT_PHASE_ID = "default";
|
|
1756
1759
|
var STOP_PHASE_ID = "stop";
|
|
1757
1760
|
var COMPACT_PHASE_ID = "compact";
|
|
@@ -1809,6 +1812,14 @@ function createCompactPhase() {
|
|
|
1809
1812
|
});
|
|
1810
1813
|
const tools = context.tools.filter((tool) => tool.core && (tool.name === "read" || tool.name === "bash"));
|
|
1811
1814
|
const working = { ...context, tools, skills: [...context.skills], messages: [...context.messages] };
|
|
1815
|
+
const instructions = compactInstructions(context);
|
|
1816
|
+
if (instructions) {
|
|
1817
|
+
working.messages.push(createMessage("user", `Additional compaction instructions:
|
|
1818
|
+
${instructions}`, {
|
|
1819
|
+
kind: "phase_input",
|
|
1820
|
+
phase: COMPACT_PHASE_ID
|
|
1821
|
+
}));
|
|
1822
|
+
}
|
|
1812
1823
|
let result = await execution.invokeModel(working, { output: "internal" });
|
|
1813
1824
|
for (let attempt = 0; attempt < 8 && result.toolCalls.length > 0; attempt += 1) {
|
|
1814
1825
|
const calls = result.toolCalls.filter((call) => tools.some((tool) => tool.name === call.name));
|
|
@@ -1860,7 +1871,11 @@ function createCompactPhase() {
|
|
|
1860
1871
|
return {
|
|
1861
1872
|
route: "stop",
|
|
1862
1873
|
phase: COMPACT_PHASE_ID,
|
|
1863
|
-
payload: {
|
|
1874
|
+
payload: {
|
|
1875
|
+
kind: "context_compaction",
|
|
1876
|
+
summary: result.text,
|
|
1877
|
+
...instructions ? { instructions } : {}
|
|
1878
|
+
},
|
|
1864
1879
|
status: {
|
|
1865
1880
|
state: "completed",
|
|
1866
1881
|
kind: "compacted",
|
|
@@ -1870,6 +1885,21 @@ function createCompactPhase() {
|
|
|
1870
1885
|
}
|
|
1871
1886
|
};
|
|
1872
1887
|
}
|
|
1888
|
+
function compactInstructions(context) {
|
|
1889
|
+
const rowan = context.execution.runMetadata?.rowan;
|
|
1890
|
+
if (typeof rowan === "object" && rowan !== null && !Array.isArray(rowan)) {
|
|
1891
|
+
const configured = rowan.instructions;
|
|
1892
|
+
if (typeof configured === "string" && configured.trim().length > 0) {
|
|
1893
|
+
return configured.trim();
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1896
|
+
const input = context.execution.input;
|
|
1897
|
+
if (input === void 0) return void 0;
|
|
1898
|
+
const text = typeof input === "string" ? input : input.filter((part) => part.type === "text").map((part) => part.text).join("");
|
|
1899
|
+
const match = text.match(/^\/compact(?:\s+([\s\S]*))?$/i);
|
|
1900
|
+
const instructions = match?.[1]?.trim();
|
|
1901
|
+
return instructions || void 0;
|
|
1902
|
+
}
|
|
1873
1903
|
function createCorePhases() {
|
|
1874
1904
|
return [createDefaultPhase(), createStopPhase(), createCompactPhase()];
|
|
1875
1905
|
}
|
|
@@ -2096,7 +2126,7 @@ function findLatestUserInputMessage(messages) {
|
|
|
2096
2126
|
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
2097
2127
|
const message = messages[index];
|
|
2098
2128
|
const kind = message.metadata?.kind;
|
|
2099
|
-
if (message.role === "user" && kind !== "phase_prompt" && kind !== "phase_input") {
|
|
2129
|
+
if (message.role === "user" && kind !== "phase_prompt" && kind !== "phase_input" && kind !== "host_context") {
|
|
2100
2130
|
return message;
|
|
2101
2131
|
}
|
|
2102
2132
|
}
|
|
@@ -3249,6 +3279,7 @@ function isContinuation(value) {
|
|
|
3249
3279
|
}
|
|
3250
3280
|
|
|
3251
3281
|
// src/runtime/contracts.ts
|
|
3282
|
+
var HOST_CONTEXT_MESSAGE_KIND = "host_context";
|
|
3252
3283
|
var THINKING_LEVELS = [
|
|
3253
3284
|
"off",
|
|
3254
3285
|
"minimal",
|
|
@@ -3271,7 +3302,7 @@ function thinkingLevelFromMessages(messages) {
|
|
|
3271
3302
|
if (message?.role !== "user") continue;
|
|
3272
3303
|
const metadata = message.metadata;
|
|
3273
3304
|
const kind = isRecord3(metadata) && typeof metadata.kind === "string" ? metadata.kind : void 0;
|
|
3274
|
-
if (kind === "phase_prompt" || kind === "phase_input") continue;
|
|
3305
|
+
if (kind === "phase_prompt" || kind === "phase_input" || kind === HOST_CONTEXT_MESSAGE_KIND) continue;
|
|
3275
3306
|
return thinkingLevelFromMetadata(metadata);
|
|
3276
3307
|
}
|
|
3277
3308
|
return void 0;
|
|
@@ -3325,6 +3356,9 @@ function normalizeUserInput(input) {
|
|
|
3325
3356
|
assertJsonValue(normalized, "input");
|
|
3326
3357
|
return normalized;
|
|
3327
3358
|
}
|
|
3359
|
+
function canonicalUserInput(input) {
|
|
3360
|
+
return canonicalJson(normalizeUserInput(input));
|
|
3361
|
+
}
|
|
3328
3362
|
function isAssistantMessage(value) {
|
|
3329
3363
|
return isRecord3(value) && hasOnlyKeys(value, ["id", "agentId", "runId", "messageRevision", "role", "content", "metadata", "sequenceWithinRun", "createdAt", "interrupted"]) && typeof value.id === "string" && typeof value.agentId === "string" && typeof value.runId === "string" && value.role === "assistant" && (value.messageRevision === void 0 || Number.isInteger(value.messageRevision) && value.messageRevision >= 0) && Number.isInteger(value.sequenceWithinRun) && value.sequenceWithinRun >= 0 && typeof value.createdAt === "string" && isAssistantContent(value.content) && (value.metadata === void 0 || isMetadata(value.metadata)) && (value.interrupted === void 0 || typeof value.interrupted === "boolean");
|
|
3330
3364
|
}
|
|
@@ -3351,6 +3385,12 @@ function assertAgentConfig(config) {
|
|
|
3351
3385
|
names.add(context.name);
|
|
3352
3386
|
assertJsonValue(context.value, `Context candidate "${context.name}" value`);
|
|
3353
3387
|
}
|
|
3388
|
+
for (const context of config.additionalContexts ?? []) {
|
|
3389
|
+
if (typeof context.name !== "string" || context.name.trim() === "") {
|
|
3390
|
+
throw new TypeError("Additional Context candidate name must be non-empty");
|
|
3391
|
+
}
|
|
3392
|
+
assertJsonValue(context.value, `Additional Context candidate "${context.name}" value`);
|
|
3393
|
+
}
|
|
3354
3394
|
}
|
|
3355
3395
|
function assertAgentConfigRequest(config) {
|
|
3356
3396
|
if (!isAgentConfiguration(config)) {
|
|
@@ -3378,6 +3418,10 @@ function assertAgentConfigRequest(config) {
|
|
|
3378
3418
|
if (typeof context.name !== "string" || context.name.trim() === "") throw new TypeError("Context candidate name must be non-empty");
|
|
3379
3419
|
assertJsonValue(context.value, `Context candidate "${context.name}" value`);
|
|
3380
3420
|
}
|
|
3421
|
+
for (const context of config.additionalContexts ?? []) {
|
|
3422
|
+
if (typeof context.name !== "string" || context.name.trim() === "") throw new TypeError("Additional Context candidate name must be non-empty");
|
|
3423
|
+
assertJsonValue(context.value, `Additional Context candidate "${context.name}" value`);
|
|
3424
|
+
}
|
|
3381
3425
|
}
|
|
3382
3426
|
function assertToolExecutionResult(value) {
|
|
3383
3427
|
if (!isToolResult(value)) throw new TypeError("Tool result must be JSON-safe and contain no Runtime identity");
|
|
@@ -3514,7 +3558,17 @@ function snapshotConfig(config) {
|
|
|
3514
3558
|
})))
|
|
3515
3559
|
} : {}
|
|
3516
3560
|
});
|
|
3517
|
-
return Object.freeze({
|
|
3561
|
+
return Object.freeze({
|
|
3562
|
+
...config,
|
|
3563
|
+
definition,
|
|
3564
|
+
resources,
|
|
3565
|
+
...config.additionalContexts ? {
|
|
3566
|
+
additionalContexts: Object.freeze(config.additionalContexts.map((context) => Object.freeze({
|
|
3567
|
+
name: context.name,
|
|
3568
|
+
value: snapshotJsonValue(context.value)
|
|
3569
|
+
})))
|
|
3570
|
+
} : {}
|
|
3571
|
+
});
|
|
3518
3572
|
}
|
|
3519
3573
|
function snapshotPhaseRegistry(registry) {
|
|
3520
3574
|
const phases = /* @__PURE__ */ new Map();
|
|
@@ -3563,6 +3617,12 @@ function snapshotConfiguration(config) {
|
|
|
3563
3617
|
name: context.name,
|
|
3564
3618
|
value: snapshotJsonValue(context.value)
|
|
3565
3619
|
})))
|
|
3620
|
+
} : {},
|
|
3621
|
+
...config.additionalContexts ? {
|
|
3622
|
+
additionalContexts: Object.freeze(config.additionalContexts.map((context) => Object.freeze({
|
|
3623
|
+
name: context.name,
|
|
3624
|
+
value: snapshotJsonValue(context.value)
|
|
3625
|
+
})))
|
|
3566
3626
|
} : {}
|
|
3567
3627
|
});
|
|
3568
3628
|
}
|
|
@@ -5474,6 +5534,8 @@ function resolveConfigurationSnapshot(registry, input) {
|
|
|
5474
5534
|
throw new Error(`Agent Definition "${input.definition.name}" is not available in the Resource View.`);
|
|
5475
5535
|
}
|
|
5476
5536
|
const layer = input.definition.layer;
|
|
5537
|
+
const selectedContexts = selectNamedResources(input.contexts ?? [], base.contexts, "Context");
|
|
5538
|
+
const additionalContexts = deduplicateContexts(input.additionalContexts ?? [], selectedContexts);
|
|
5477
5539
|
const definition = applyDefinitionLayer(base, layer);
|
|
5478
5540
|
const tools = selectNamedResources(resolved.tools, definition.tools, "Tool");
|
|
5479
5541
|
const skills = mergeSkills(
|
|
@@ -5481,7 +5543,6 @@ function resolveConfigurationSnapshot(registry, input) {
|
|
|
5481
5543
|
definition.bundledSkills
|
|
5482
5544
|
);
|
|
5483
5545
|
const phases = resolvePhases(resolved.phases, definition.phases);
|
|
5484
|
-
const contexts = selectNamedResources(input.contexts ?? [], base.contexts, "Context");
|
|
5485
5546
|
return {
|
|
5486
5547
|
identity: input.identity,
|
|
5487
5548
|
definition,
|
|
@@ -5497,7 +5558,8 @@ function resolveConfigurationSnapshot(registry, input) {
|
|
|
5497
5558
|
phase: selectedRefs(resolved.refs.phase, [...phases?.phases.keys() ?? []])
|
|
5498
5559
|
}
|
|
5499
5560
|
},
|
|
5500
|
-
contexts,
|
|
5561
|
+
contexts: selectedContexts,
|
|
5562
|
+
additionalContexts,
|
|
5501
5563
|
resourceView: input.resourceView,
|
|
5502
5564
|
...input.cwd === void 0 ? {} : { cwd: input.cwd },
|
|
5503
5565
|
...input.maxAttempts === void 0 ? {} : { maxAttempts: input.maxAttempts },
|
|
@@ -5522,6 +5584,7 @@ function materializeConfigurationSnapshot(snapshot) {
|
|
|
5522
5584
|
],
|
|
5523
5585
|
resourceRevisions: snapshot.resources.revisions
|
|
5524
5586
|
},
|
|
5587
|
+
...snapshot.additionalContexts.length > 0 ? { additionalContexts: snapshot.additionalContexts } : {},
|
|
5525
5588
|
...snapshot.cwd === void 0 ? {} : { cwd: snapshot.cwd },
|
|
5526
5589
|
...snapshot.maxAttempts === void 0 ? {} : { maxAttempts: snapshot.maxAttempts },
|
|
5527
5590
|
..."stream" in snapshot && snapshot.stream ? { model: snapshot.model, stream: snapshot.stream } : { model: snapshot.model }
|
|
@@ -5539,6 +5602,16 @@ function applyDefinitionLayer(base, layer) {
|
|
|
5539
5602
|
...layer.phases === void 0 ? {} : { phases: intersectPhaseSelection(base.phases, layer.phases) }
|
|
5540
5603
|
};
|
|
5541
5604
|
}
|
|
5605
|
+
function deduplicateContexts(additional, existing = []) {
|
|
5606
|
+
const contexts = [];
|
|
5607
|
+
const seen = new Set(existing.map(({ name }) => name));
|
|
5608
|
+
for (const context of additional) {
|
|
5609
|
+
if (seen.has(context.name)) continue;
|
|
5610
|
+
seen.add(context.name);
|
|
5611
|
+
contexts.push(context);
|
|
5612
|
+
}
|
|
5613
|
+
return contexts;
|
|
5614
|
+
}
|
|
5542
5615
|
function intersectNames(parent, layer, kind) {
|
|
5543
5616
|
if (parent === void 0) return [...layer];
|
|
5544
5617
|
const parentNames = new Set(parent);
|
|
@@ -5735,7 +5808,9 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
5735
5808
|
};
|
|
5736
5809
|
const run = await this.owned.createRun({
|
|
5737
5810
|
agentId,
|
|
5738
|
-
input
|
|
5811
|
+
// An empty input is a system-triggered Control Run. A non-empty input
|
|
5812
|
+
// represents a user invocation and is committed by the Durable Store.
|
|
5813
|
+
input: options.input ?? "",
|
|
5739
5814
|
metadata,
|
|
5740
5815
|
idempotencyKey: options.idempotencyKey ?? createId("compact")
|
|
5741
5816
|
});
|
|
@@ -5974,7 +6049,13 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
5974
6049
|
}
|
|
5975
6050
|
}
|
|
5976
6051
|
const executionId = createId("exec");
|
|
5977
|
-
claim = await this.owned.claimRun({
|
|
6052
|
+
claim = await this.owned.claimRun({
|
|
6053
|
+
runId: run.id,
|
|
6054
|
+
expectedRevision: run.revision,
|
|
6055
|
+
executionId,
|
|
6056
|
+
configToken: token,
|
|
6057
|
+
...controlKind ? {} : { inputContext: additionalContextInput(config) }
|
|
6058
|
+
});
|
|
5978
6059
|
executionRevision = claim.run.revision;
|
|
5979
6060
|
const controller = new AbortController();
|
|
5980
6061
|
this.executions.set(run.id, { controller, executionId });
|
|
@@ -6002,17 +6083,6 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
6002
6083
|
...executionContext2.phases,
|
|
6003
6084
|
entryPhaseId: COMPACT_PHASE_ID
|
|
6004
6085
|
};
|
|
6005
|
-
const instructions = controlKind === "compact" ? compactInstructions(run) : void 0;
|
|
6006
|
-
if (instructions) {
|
|
6007
|
-
executionContext2.messages.push({
|
|
6008
|
-
id: createId("msg"),
|
|
6009
|
-
role: "user",
|
|
6010
|
-
content: `Additional compaction instructions:
|
|
6011
|
-
${instructions}`,
|
|
6012
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6013
|
-
metadata: { kind: "phase_input", phase: COMPACT_PHASE_ID }
|
|
6014
|
-
});
|
|
6015
|
-
}
|
|
6016
6086
|
}
|
|
6017
6087
|
return executionContext2;
|
|
6018
6088
|
};
|
|
@@ -6029,6 +6099,7 @@ ${instructions}`,
|
|
|
6029
6099
|
agentId: run.agentId,
|
|
6030
6100
|
runId: run.id,
|
|
6031
6101
|
executionId: claim.execution.executionId,
|
|
6102
|
+
input: typeof run.input === "string" ? run.input : run.input.content,
|
|
6032
6103
|
...agent.metadata === void 0 ? {} : { agentMetadata: agent.metadata },
|
|
6033
6104
|
...run.metadata === void 0 ? {} : { runMetadata: run.metadata }
|
|
6034
6105
|
},
|
|
@@ -6175,6 +6246,7 @@ ${instructions}`,
|
|
|
6175
6246
|
const output = latestAssistant(run, result.messages.slice(modelMessages.length), modelMessages.length);
|
|
6176
6247
|
if (controlKind === "compact" || isCompactionOutcome(result.outcome.payload)) {
|
|
6177
6248
|
const summary = compactSummary(result.outcome.payload);
|
|
6249
|
+
const instructions = compactInstructions2(run) ?? compactOutputInstructions(result.outcome.payload);
|
|
6178
6250
|
if (summary) {
|
|
6179
6251
|
const covered = claim.history.at(-1);
|
|
6180
6252
|
const record = {
|
|
@@ -6182,7 +6254,7 @@ ${instructions}`,
|
|
|
6182
6254
|
agentId: run.agentId,
|
|
6183
6255
|
summary,
|
|
6184
6256
|
...covered ? { coveredThrough: { messageId: covered.id, sequence: covered.sequenceWithinRun } } : {},
|
|
6185
|
-
...
|
|
6257
|
+
...instructions ? { instructions } : {},
|
|
6186
6258
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6187
6259
|
};
|
|
6188
6260
|
await this.owned.commitContextCompaction(record);
|
|
@@ -6452,6 +6524,12 @@ ${instructions}`,
|
|
|
6452
6524
|
this.heartbeat.unref?.();
|
|
6453
6525
|
}
|
|
6454
6526
|
};
|
|
6527
|
+
function additionalContextInput(config) {
|
|
6528
|
+
const contexts = config.additionalContexts ?? [];
|
|
6529
|
+
if (contexts.length === 0) return void 0;
|
|
6530
|
+
const content = buildAdditionalContextMessage(contexts);
|
|
6531
|
+
return content.length === 0 ? void 0 : { content, metadata: { kind: HOST_CONTEXT_MESSAGE_KIND } };
|
|
6532
|
+
}
|
|
6455
6533
|
var DurableRun = class {
|
|
6456
6534
|
constructor(runtime, id) {
|
|
6457
6535
|
this.runtime = runtime;
|
|
@@ -6527,7 +6605,7 @@ function controlRunKind(run) {
|
|
|
6527
6605
|
const kind = rowan.kind;
|
|
6528
6606
|
return typeof kind === "string" ? kind : void 0;
|
|
6529
6607
|
}
|
|
6530
|
-
function
|
|
6608
|
+
function compactInstructions2(run) {
|
|
6531
6609
|
const rowan = run.metadata?.rowan;
|
|
6532
6610
|
if (typeof rowan !== "object" || rowan === null || !("instructions" in rowan)) return void 0;
|
|
6533
6611
|
const instructions = rowan.instructions;
|
|
@@ -6538,6 +6616,11 @@ function compactSummary(payload) {
|
|
|
6538
6616
|
const summary = payload.summary;
|
|
6539
6617
|
return typeof summary === "string" && summary.trim().length > 0 ? summary : void 0;
|
|
6540
6618
|
}
|
|
6619
|
+
function compactOutputInstructions(payload) {
|
|
6620
|
+
if (typeof payload !== "object" || payload === null || !("instructions" in payload)) return void 0;
|
|
6621
|
+
const instructions = payload.instructions;
|
|
6622
|
+
return typeof instructions === "string" && instructions.trim().length > 0 ? instructions.trim() : void 0;
|
|
6623
|
+
}
|
|
6541
6624
|
function isCompactionOutcome(payload) {
|
|
6542
6625
|
return typeof payload === "object" && payload !== null && "kind" in payload && payload.kind === "context_compaction";
|
|
6543
6626
|
}
|
|
@@ -6995,7 +7078,14 @@ var InMemoryStore = class _InMemoryStore {
|
|
|
6995
7078
|
this.assertOwner(lease);
|
|
6996
7079
|
const executionId = input.executionId ?? createId("exec");
|
|
6997
7080
|
const operationKey = `claim:${executionId}`;
|
|
6998
|
-
const
|
|
7081
|
+
const inputContext = input.inputContext === void 0 ? void 0 : normalizeUserInput(input.inputContext);
|
|
7082
|
+
const operationPayload = canonicalJson([
|
|
7083
|
+
input.runId,
|
|
7084
|
+
input.expectedRevision,
|
|
7085
|
+
input.messageId ?? null,
|
|
7086
|
+
input.configToken ?? null,
|
|
7087
|
+
inputContext ?? null
|
|
7088
|
+
]);
|
|
6999
7089
|
const replay = this.replayOperation(operationKey, operationPayload);
|
|
7000
7090
|
if (replay) return clone(replay);
|
|
7001
7091
|
const run = this.requireRun(input.runId);
|
|
@@ -7011,10 +7101,25 @@ var InMemoryStore = class _InMemoryStore {
|
|
|
7011
7101
|
throw new RuntimeError("run_state_conflict", { runId: run.id, expected: ["queued"], actual: run.state });
|
|
7012
7102
|
}
|
|
7013
7103
|
if (!run.pinnedConfigToken && input.configToken) run.pinnedConfigToken = input.configToken;
|
|
7014
|
-
|
|
7015
|
-
|
|
7104
|
+
const committedMessages = [];
|
|
7105
|
+
const existingMessages = this.messagesForRun(run.id);
|
|
7106
|
+
if (inputContext && hasUserInput(inputContext) && !isControlRun(run) && !existingMessages.some((message) => message.role === "user" && message.metadata?.kind === HOST_CONTEXT_MESSAGE_KIND && canonicalUserInput({ content: message.content, metadata: message.metadata }) === canonicalUserInput(inputContext))) {
|
|
7107
|
+
const contextMessage = {
|
|
7108
|
+
id: createId("msg"),
|
|
7109
|
+
agentId: run.agentId,
|
|
7110
|
+
runId: run.id,
|
|
7111
|
+
role: "user",
|
|
7112
|
+
content: userInputContent(inputContext),
|
|
7113
|
+
...userInputMetadata(inputContext) ? { metadata: clone(userInputMetadata(inputContext)) } : {},
|
|
7114
|
+
sequenceWithinRun: this.nextMessageSequence(run.id),
|
|
7115
|
+
createdAt: createTimestamp()
|
|
7116
|
+
};
|
|
7117
|
+
this.messages.set(contextMessage.id, contextMessage);
|
|
7118
|
+
committedMessages.push(contextMessage);
|
|
7119
|
+
}
|
|
7120
|
+
if (!run.checkpoint && !run.initialMessageId && (!isControlRun(run) || hasUserInput(run.input))) {
|
|
7016
7121
|
const userInput = normalizeUserInput(run.input);
|
|
7017
|
-
message = {
|
|
7122
|
+
const message = {
|
|
7018
7123
|
id: input.messageId ?? createId("msg"),
|
|
7019
7124
|
agentId: run.agentId,
|
|
7020
7125
|
runId: run.id,
|
|
@@ -7025,6 +7130,7 @@ var InMemoryStore = class _InMemoryStore {
|
|
|
7025
7130
|
createdAt: createTimestamp()
|
|
7026
7131
|
};
|
|
7027
7132
|
this.messages.set(message.id, message);
|
|
7133
|
+
committedMessages.push(message);
|
|
7028
7134
|
}
|
|
7029
7135
|
const execution = {
|
|
7030
7136
|
runId: run.id,
|
|
@@ -7035,7 +7141,7 @@ var InMemoryStore = class _InMemoryStore {
|
|
|
7035
7141
|
run.execution = execution;
|
|
7036
7142
|
run.revision += 1;
|
|
7037
7143
|
run.updatedAt = createTimestamp();
|
|
7038
|
-
|
|
7144
|
+
for (const message of committedMessages) this.appendMessage(run, message);
|
|
7039
7145
|
this.appendTransition(run, "queued", "running");
|
|
7040
7146
|
const result = { run: clone(run), execution: clone(execution), history: this.activeHistory(run.agentId, run.agentSequence) };
|
|
7041
7147
|
this.writeOperationReceipt(operationKey, operationPayload, result);
|
|
@@ -7930,12 +8036,16 @@ function ownershipLost(expected, actual, reason) {
|
|
|
7930
8036
|
function userInputContent(input) {
|
|
7931
8037
|
return typeof input === "string" ? input : input.content;
|
|
7932
8038
|
}
|
|
8039
|
+
function hasUserInput(input) {
|
|
8040
|
+
const content = userInputContent(input);
|
|
8041
|
+
return content.length > 0;
|
|
8042
|
+
}
|
|
7933
8043
|
function userInputMetadata(input) {
|
|
7934
8044
|
return typeof input === "string" ? void 0 : input.metadata;
|
|
7935
8045
|
}
|
|
7936
8046
|
function isControlRun(run) {
|
|
7937
8047
|
const rowan = run.metadata?.rowan;
|
|
7938
|
-
return typeof rowan === "object" && rowan !== null &&
|
|
8048
|
+
return typeof rowan === "object" && rowan !== null && typeof rowan.kind === "string" && rowan.kind.length > 0;
|
|
7939
8049
|
}
|
|
7940
8050
|
function estimateMessageTokens(messages) {
|
|
7941
8051
|
let characters = 0;
|