@gajae-code/agent-core 0.2.5 → 0.3.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/CHANGELOG.md +10 -0
- package/dist/types/agent.d.ts +18 -0
- package/dist/types/telemetry.d.ts +13 -5
- package/dist/types/types.d.ts +11 -0
- package/package.json +4 -4
- package/src/agent-loop.ts +20 -3
- package/src/agent.ts +44 -0
- package/src/telemetry.ts +51 -20
- package/src/types.ts +11 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.3.1] - 2026-06-05
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Moved the env-driven full message content capture warning into agent-core telemetry resolution so direct `@gajae-code/agent-core` consumers receive `full_content_capture_env_active` when `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=full` is used without an explicit `captureMessageContent` override.
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- Clarified that telemetry `summary` content capture emits bounded real-content snippets in `pi.gen_ai.*` attributes and is not redacted or PII-free.
|
|
14
|
+
|
|
5
15
|
## [0.2.4] - 2026-06-02
|
|
6
16
|
|
|
7
17
|
### Added
|
package/dist/types/agent.d.ts
CHANGED
|
@@ -34,6 +34,8 @@ export interface AgentOptions {
|
|
|
34
34
|
* - "wait": defer steering until the current turn completes
|
|
35
35
|
*/
|
|
36
36
|
interruptMode?: "immediate" | "wait";
|
|
37
|
+
/** Cooperative pause checkpoint passed through to AgentLoopConfig.shouldPause. */
|
|
38
|
+
shouldPause?: AgentLoopConfig["shouldPause"];
|
|
37
39
|
/**
|
|
38
40
|
* API format for Kimi Code provider: "openai" or "anthropic" (default: "anthropic")
|
|
39
41
|
*/
|
|
@@ -289,6 +291,7 @@ export declare class Agent {
|
|
|
289
291
|
setRawSseEventInterceptor(fn: SimpleStreamOptions["onSseEvent"] | undefined): void;
|
|
290
292
|
setAssistantMessageEventInterceptor(fn: ((message: AssistantMessage, event: AssistantMessageEvent) => void) | undefined): void;
|
|
291
293
|
setOnBeforeYield(fn: (() => Promise<void> | void) | undefined): void;
|
|
294
|
+
setShouldPause(fn: AgentLoopConfig["shouldPause"] | undefined): void;
|
|
292
295
|
emitExternalEvent(event: AgentEvent): void;
|
|
293
296
|
createExternalEventEmitterForCurrentRun(): ((event: AgentEvent) => void) | undefined;
|
|
294
297
|
setSystemPrompt(v: string[]): void;
|
|
@@ -318,6 +321,21 @@ export declare class Agent {
|
|
|
318
321
|
clearFollowUpQueue(): void;
|
|
319
322
|
clearAllQueues(): void;
|
|
320
323
|
hasQueuedMessages(): boolean;
|
|
324
|
+
hasQueuedSteering(): boolean;
|
|
325
|
+
/**
|
|
326
|
+
* Snapshot the steering queue without mutating it. Used to preserve queued
|
|
327
|
+
* steering across maintenance ops (compaction/handoff) that call reset().
|
|
328
|
+
*/
|
|
329
|
+
snapshotSteering(): AgentMessage[];
|
|
330
|
+
/**
|
|
331
|
+
* Restore previously snapshotted steering messages ahead of any newly
|
|
332
|
+
* queued ones. No-op for an empty snapshot.
|
|
333
|
+
*/
|
|
334
|
+
restoreSteering(messages: AgentMessage[]): void;
|
|
335
|
+
/** Snapshot the follow-up queue without mutating it. */
|
|
336
|
+
snapshotFollowUp(): AgentMessage[];
|
|
337
|
+
/** Restore previously snapshotted follow-up messages ahead of any newly queued ones. */
|
|
338
|
+
restoreFollowUp(messages: AgentMessage[]): void;
|
|
321
339
|
/**
|
|
322
340
|
* Remove and return the last steering message from the queue (LIFO).
|
|
323
341
|
* Used by dequeue keybinding.
|
|
@@ -211,8 +211,9 @@ export interface AgentIdentity {
|
|
|
211
211
|
readonly name?: string;
|
|
212
212
|
readonly description?: string;
|
|
213
213
|
}
|
|
214
|
+
export type AgentTelemetryWarningCode = "resolve_attributes_failed" | "content_serializer_failed" | "on_cost_delta_failed" | "on_chat_usage_failed" | "cost_estimator_failed" | "on_run_end_failed" | "on_span_start_failed" | "on_span_end_failed" | "normalize_agent_name_failed" | "normalize_provider_failed" | "full_content_capture_env_active" | "on_telemetry_warning_failed";
|
|
214
215
|
export interface AgentTelemetryWarning {
|
|
215
|
-
readonly code:
|
|
216
|
+
readonly code: AgentTelemetryWarningCode;
|
|
216
217
|
readonly message: string;
|
|
217
218
|
readonly error?: unknown;
|
|
218
219
|
}
|
|
@@ -249,12 +250,18 @@ export interface AgentTelemetryConfig {
|
|
|
249
250
|
/** Override the tracer name passed to `trace.getTracer`. */
|
|
250
251
|
readonly tracerName?: string;
|
|
251
252
|
/**
|
|
252
|
-
* Capture request/response content. `true` preserves the
|
|
253
|
-
* payload capture
|
|
254
|
-
* `"
|
|
253
|
+
* Capture request/response content. Programmatic `true` preserves the
|
|
254
|
+
* historical full payload capture because code config is an explicit opt-in;
|
|
255
|
+
* `"summary"` emits bounded dashboard-friendly summaries in
|
|
256
|
+
* `pi.gen_ai.request.messages`, `pi.gen_ai.response.text`, and related
|
|
257
|
+
* `pi.gen_ai.*` attributes. Summary mode is not redacted or PII-free: text is
|
|
258
|
+
* truncated to bounded snippets, not removed. `"full"` emits both summaries
|
|
259
|
+
* and full OTEL message payloads.
|
|
255
260
|
*
|
|
256
261
|
* Defaults to the value of the `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT`
|
|
257
|
-
* env var (`true`/`1`/`yes` => `"
|
|
262
|
+
* env var (`true`/`1`/`yes` => `"summary"`, `"summary"` => `"summary"`,
|
|
263
|
+
* `"full"` => `"full"`). Env-driven `"full"` capture emits a
|
|
264
|
+
* `full_content_capture_env_active` telemetry warning once per process.
|
|
258
265
|
*/
|
|
259
266
|
readonly captureMessageContent?: TelemetryContentCapture;
|
|
260
267
|
/** Extra attributes merged onto every emitted span. */
|
|
@@ -335,6 +342,7 @@ export interface AgentTelemetry {
|
|
|
335
342
|
}
|
|
336
343
|
/** Lazily resolve the {@link AgentTelemetry} handle. Returns `undefined` when disabled. */
|
|
337
344
|
export declare function resolveTelemetry(config: AgentTelemetryConfig | undefined, sessionId: string | undefined): AgentTelemetry | undefined;
|
|
345
|
+
export declare function resetContentCaptureEnvCacheForTest(): void;
|
|
338
346
|
export declare function recordTelemetryWarning(telemetry: AgentTelemetry | undefined, warning: AgentTelemetryWarning): void;
|
|
339
347
|
/**
|
|
340
348
|
* Start the outer `invoke_agent` span that wraps a full `runLoop` invocation.
|
package/dist/types/types.d.ts
CHANGED
|
@@ -103,6 +103,15 @@ export interface AgentLoopConfig extends SimpleStreamOptions {
|
|
|
103
103
|
* continues with another turn.
|
|
104
104
|
*/
|
|
105
105
|
getFollowUpMessages?: () => Promise<AgentMessage[]>;
|
|
106
|
+
/**
|
|
107
|
+
* Cooperative pause checkpoint evaluated at safe loop boundaries.
|
|
108
|
+
*
|
|
109
|
+
* Called after completed tool execution has been emitted and before the loop
|
|
110
|
+
* polls steering/follow-up queues or schedules another assistant response.
|
|
111
|
+
* Returning true ends the current loop with `agent_end.stopReason === "paused"`
|
|
112
|
+
* without aborting any in-flight model or tool work.
|
|
113
|
+
*/
|
|
114
|
+
shouldPause?: () => boolean;
|
|
106
115
|
/**
|
|
107
116
|
* Hook fired right before the loop would exit.
|
|
108
117
|
*
|
|
@@ -375,6 +384,8 @@ export type AgentEvent = {
|
|
|
375
384
|
} | {
|
|
376
385
|
type: "agent_end";
|
|
377
386
|
messages: AgentMessage[];
|
|
387
|
+
/** Indicates whether the loop ended normally or suspended at a pause checkpoint. */
|
|
388
|
+
stopReason?: "completed" | "paused";
|
|
378
389
|
/** Present iff `AgentTelemetryConfig` was supplied on this run. */
|
|
379
390
|
telemetry?: AgentRunSummary;
|
|
380
391
|
coverage?: AgentRunCoverage;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@gajae-code/agent-core",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.3.1",
|
|
5
5
|
"description": "General-purpose agent with transport abstraction, state management, and attachment support",
|
|
6
6
|
"homepage": "https://gaebal-gajae.dev",
|
|
7
7
|
"author": "Yeachan-Heo",
|
|
@@ -35,9 +35,9 @@
|
|
|
35
35
|
"fmt": "biome format --write ."
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@gajae-code/ai": "0.
|
|
39
|
-
"@gajae-code/natives": "0.
|
|
40
|
-
"@gajae-code/utils": "0.
|
|
38
|
+
"@gajae-code/ai": "0.3.1",
|
|
39
|
+
"@gajae-code/natives": "0.3.1",
|
|
40
|
+
"@gajae-code/utils": "0.3.1",
|
|
41
41
|
"@opentelemetry/api": "^1.9.0"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
package/src/agent-loop.ts
CHANGED
|
@@ -203,13 +203,15 @@ function buildAgentEndEvent(
|
|
|
203
203
|
messages: AgentMessage[],
|
|
204
204
|
telemetry: AgentTelemetry | undefined,
|
|
205
205
|
stepCount: number,
|
|
206
|
+
stopReason: "completed" | "paused" = "completed",
|
|
206
207
|
): Extract<AgentEvent, { type: "agent_end" }> {
|
|
207
|
-
|
|
208
|
+
const base = { type: "agent_end" as const, messages, stopReason };
|
|
209
|
+
if (!telemetry) return base;
|
|
208
210
|
const snapshot = telemetry.collector.snapshot({ stepCount });
|
|
209
211
|
if (telemetry.collector.markRunEnded()) {
|
|
210
212
|
fireOnRunEnd(telemetry, snapshot.summary, snapshot.coverage);
|
|
211
213
|
}
|
|
212
|
-
return {
|
|
214
|
+
return { ...base, telemetry: snapshot.summary, coverage: snapshot.coverage };
|
|
213
215
|
}
|
|
214
216
|
|
|
215
217
|
/**
|
|
@@ -585,11 +587,26 @@ async function runLoopBody(
|
|
|
585
587
|
|
|
586
588
|
stream.push({ type: "turn_end", message, toolResults });
|
|
587
589
|
|
|
588
|
-
|
|
590
|
+
if (steeringMessagesFromExecution && steeringMessagesFromExecution.length > 0) {
|
|
591
|
+
pendingMessages = steeringMessagesFromExecution;
|
|
592
|
+
continue;
|
|
593
|
+
}
|
|
594
|
+
pendingMessages = (await config.getSteeringMessages?.()) || [];
|
|
595
|
+
if (pendingMessages.length > 0) continue;
|
|
596
|
+
if (config.shouldPause?.()) {
|
|
597
|
+
stream.push(buildAgentEndEvent(newMessages, telemetry, stepCounter.count, "paused"));
|
|
598
|
+
stream.end(newMessages);
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
589
601
|
}
|
|
590
602
|
|
|
591
603
|
// Agent would stop here. Check for follow-up messages.
|
|
592
604
|
await config.onBeforeYield?.();
|
|
605
|
+
if (config.shouldPause?.()) {
|
|
606
|
+
stream.push(buildAgentEndEvent(newMessages, telemetry, stepCounter.count, "paused"));
|
|
607
|
+
stream.end(newMessages);
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
593
610
|
const followUpMessages = (await config.getFollowUpMessages?.()) || [];
|
|
594
611
|
if (followUpMessages.length > 0) {
|
|
595
612
|
// Set as pending so inner loop processes them
|
package/src/agent.ts
CHANGED
|
@@ -99,6 +99,8 @@ export interface AgentOptions {
|
|
|
99
99
|
* - "wait": defer steering until the current turn completes
|
|
100
100
|
*/
|
|
101
101
|
interruptMode?: "immediate" | "wait";
|
|
102
|
+
/** Cooperative pause checkpoint passed through to AgentLoopConfig.shouldPause. */
|
|
103
|
+
shouldPause?: AgentLoopConfig["shouldPause"];
|
|
102
104
|
|
|
103
105
|
/**
|
|
104
106
|
* API format for Kimi Code provider: "openai" or "anthropic" (default: "anthropic")
|
|
@@ -309,6 +311,7 @@ export class Agent {
|
|
|
309
311
|
#onAssistantMessageEvent?: (message: AssistantMessage, event: AssistantMessageEvent) => void;
|
|
310
312
|
#onHarmonyLeak?: (event: HarmonyAuditEvent) => void | Promise<void>;
|
|
311
313
|
#onBeforeYield?: () => Promise<void> | void;
|
|
314
|
+
#shouldPause?: AgentLoopConfig["shouldPause"];
|
|
312
315
|
#telemetry?: AgentLoopConfig["telemetry"];
|
|
313
316
|
#appendOnlyContext?: AppendOnlyContextManager;
|
|
314
317
|
|
|
@@ -371,6 +374,7 @@ export class Agent {
|
|
|
371
374
|
this.#getToolChoice = opts.getToolChoice;
|
|
372
375
|
this.#onAssistantMessageEvent = opts.onAssistantMessageEvent;
|
|
373
376
|
this.#onHarmonyLeak = opts.onHarmonyLeak;
|
|
377
|
+
this.#shouldPause = opts.shouldPause;
|
|
374
378
|
this.beforeToolCall = opts.beforeToolCall;
|
|
375
379
|
this.afterToolCall = opts.afterToolCall;
|
|
376
380
|
this.#telemetry = opts.telemetry;
|
|
@@ -625,6 +629,10 @@ export class Agent {
|
|
|
625
629
|
this.#onBeforeYield = fn;
|
|
626
630
|
}
|
|
627
631
|
|
|
632
|
+
setShouldPause(fn: AgentLoopConfig["shouldPause"] | undefined): void {
|
|
633
|
+
this.#shouldPause = fn;
|
|
634
|
+
}
|
|
635
|
+
|
|
628
636
|
emitExternalEvent(event: AgentEvent) {
|
|
629
637
|
switch (event.type) {
|
|
630
638
|
case "message_start":
|
|
@@ -859,6 +867,38 @@ export class Agent {
|
|
|
859
867
|
return this.#steeringQueue.length > 0 || this.#followUpQueue.length > 0;
|
|
860
868
|
}
|
|
861
869
|
|
|
870
|
+
hasQueuedSteering(): boolean {
|
|
871
|
+
return this.#steeringQueue.length > 0;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
/**
|
|
875
|
+
* Snapshot the steering queue without mutating it. Used to preserve queued
|
|
876
|
+
* steering across maintenance ops (compaction/handoff) that call reset().
|
|
877
|
+
*/
|
|
878
|
+
snapshotSteering(): AgentMessage[] {
|
|
879
|
+
return this.#steeringQueue.slice();
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
/**
|
|
883
|
+
* Restore previously snapshotted steering messages ahead of any newly
|
|
884
|
+
* queued ones. No-op for an empty snapshot.
|
|
885
|
+
*/
|
|
886
|
+
restoreSteering(messages: AgentMessage[]): void {
|
|
887
|
+
if (messages.length === 0) return;
|
|
888
|
+
this.#steeringQueue = [...messages, ...this.#steeringQueue];
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
/** Snapshot the follow-up queue without mutating it. */
|
|
892
|
+
snapshotFollowUp(): AgentMessage[] {
|
|
893
|
+
return this.#followUpQueue.slice();
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
/** Restore previously snapshotted follow-up messages ahead of any newly queued ones. */
|
|
897
|
+
restoreFollowUp(messages: AgentMessage[]): void {
|
|
898
|
+
if (messages.length === 0) return;
|
|
899
|
+
this.#followUpQueue = [...messages, ...this.#followUpQueue];
|
|
900
|
+
}
|
|
901
|
+
|
|
862
902
|
#dequeueSteeringMessages(): AgentMessage[] {
|
|
863
903
|
if (this.#steeringMode === "one-at-a-time") {
|
|
864
904
|
if (this.#steeringQueue.length > 0) {
|
|
@@ -1196,6 +1236,10 @@ export class Agent {
|
|
|
1196
1236
|
if (this.#activeRunId !== runId) return;
|
|
1197
1237
|
await this.#onBeforeYield?.();
|
|
1198
1238
|
},
|
|
1239
|
+
shouldPause: () => {
|
|
1240
|
+
if (this.#activeRunId !== runId) return false;
|
|
1241
|
+
return this.#shouldPause?.() === true;
|
|
1242
|
+
},
|
|
1199
1243
|
telemetry: this.#telemetry,
|
|
1200
1244
|
};
|
|
1201
1245
|
|
package/src/telemetry.ts
CHANGED
|
@@ -55,6 +55,9 @@ export const DEFAULT_TRACER_NAME = "@gajae-code/agent-core";
|
|
|
55
55
|
|
|
56
56
|
/** Env var matching the OTEL semconv content-capture toggle. */
|
|
57
57
|
const CONTENT_CAPTURE_ENV = "OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT";
|
|
58
|
+
const FULL_CONTENT_CAPTURE_ENV_WARNING =
|
|
59
|
+
`${CONTENT_CAPTURE_ENV}=full enables full GenAI message content capture. ` +
|
|
60
|
+
`Use ${CONTENT_CAPTURE_ENV}=summary for bounded telemetry summaries.`;
|
|
58
61
|
|
|
59
62
|
const MAX_TELEMETRY_ARRAY_ITEMS = 64;
|
|
60
63
|
const MAX_TELEMETRY_MESSAGE_COUNT = 16;
|
|
@@ -271,19 +274,22 @@ export interface AgentIdentity {
|
|
|
271
274
|
readonly description?: string;
|
|
272
275
|
}
|
|
273
276
|
|
|
277
|
+
export type AgentTelemetryWarningCode =
|
|
278
|
+
| "resolve_attributes_failed"
|
|
279
|
+
| "content_serializer_failed"
|
|
280
|
+
| "on_cost_delta_failed"
|
|
281
|
+
| "on_chat_usage_failed"
|
|
282
|
+
| "cost_estimator_failed"
|
|
283
|
+
| "on_run_end_failed"
|
|
284
|
+
| "on_span_start_failed"
|
|
285
|
+
| "on_span_end_failed"
|
|
286
|
+
| "normalize_agent_name_failed"
|
|
287
|
+
| "normalize_provider_failed"
|
|
288
|
+
| "full_content_capture_env_active"
|
|
289
|
+
| "on_telemetry_warning_failed";
|
|
290
|
+
|
|
274
291
|
export interface AgentTelemetryWarning {
|
|
275
|
-
readonly code:
|
|
276
|
-
| "resolve_attributes_failed"
|
|
277
|
-
| "content_serializer_failed"
|
|
278
|
-
| "on_cost_delta_failed"
|
|
279
|
-
| "on_chat_usage_failed"
|
|
280
|
-
| "cost_estimator_failed"
|
|
281
|
-
| "on_run_end_failed"
|
|
282
|
-
| "on_span_start_failed"
|
|
283
|
-
| "on_span_end_failed"
|
|
284
|
-
| "normalize_agent_name_failed"
|
|
285
|
-
| "normalize_provider_failed"
|
|
286
|
-
| "on_telemetry_warning_failed";
|
|
292
|
+
readonly code: AgentTelemetryWarningCode;
|
|
287
293
|
readonly message: string;
|
|
288
294
|
readonly error?: unknown;
|
|
289
295
|
}
|
|
@@ -322,12 +328,18 @@ export interface AgentTelemetryConfig {
|
|
|
322
328
|
/** Override the tracer name passed to `trace.getTracer`. */
|
|
323
329
|
readonly tracerName?: string;
|
|
324
330
|
/**
|
|
325
|
-
* Capture request/response content. `true` preserves the
|
|
326
|
-
* payload capture
|
|
327
|
-
* `"
|
|
331
|
+
* Capture request/response content. Programmatic `true` preserves the
|
|
332
|
+
* historical full payload capture because code config is an explicit opt-in;
|
|
333
|
+
* `"summary"` emits bounded dashboard-friendly summaries in
|
|
334
|
+
* `pi.gen_ai.request.messages`, `pi.gen_ai.response.text`, and related
|
|
335
|
+
* `pi.gen_ai.*` attributes. Summary mode is not redacted or PII-free: text is
|
|
336
|
+
* truncated to bounded snippets, not removed. `"full"` emits both summaries
|
|
337
|
+
* and full OTEL message payloads.
|
|
328
338
|
*
|
|
329
339
|
* Defaults to the value of the `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT`
|
|
330
|
-
* env var (`true`/`1`/`yes` => `"
|
|
340
|
+
* env var (`true`/`1`/`yes` => `"summary"`, `"summary"` => `"summary"`,
|
|
341
|
+
* `"full"` => `"full"`). Env-driven `"full"` capture emits a
|
|
342
|
+
* `full_content_capture_env_active` telemetry warning once per process.
|
|
331
343
|
*/
|
|
332
344
|
readonly captureMessageContent?: TelemetryContentCapture;
|
|
333
345
|
/** Extra attributes merged onto every emitted span. */
|
|
@@ -415,8 +427,9 @@ export function resolveTelemetry(
|
|
|
415
427
|
): AgentTelemetry | undefined {
|
|
416
428
|
if (!config) return undefined;
|
|
417
429
|
const tracer = config.tracer ?? trace.getTracer(config.tracerName ?? DEFAULT_TRACER_NAME);
|
|
430
|
+
const contentCaptureFromEnv = config.captureMessageContent === undefined;
|
|
418
431
|
const contentCapture = resolveContentCapture(config.captureMessageContent);
|
|
419
|
-
|
|
432
|
+
const telemetry = {
|
|
420
433
|
config,
|
|
421
434
|
tracer,
|
|
422
435
|
captureMessageContent: contentCapture === "full",
|
|
@@ -425,9 +438,12 @@ export function resolveTelemetry(
|
|
|
425
438
|
agent: config.agent,
|
|
426
439
|
collector: new AgentRunCollector(),
|
|
427
440
|
};
|
|
441
|
+
warnIfEnvFullContentCaptureActive(telemetry, contentCaptureFromEnv);
|
|
442
|
+
return telemetry;
|
|
428
443
|
}
|
|
429
444
|
|
|
430
445
|
let contentCaptureEnvCache: ResolvedTelemetryContentCapture | undefined;
|
|
446
|
+
let hasWarnedFullContentCaptureEnv = false;
|
|
431
447
|
function readContentCaptureEnv(): ResolvedTelemetryContentCapture {
|
|
432
448
|
if (contentCaptureEnvCache !== undefined) return contentCaptureEnvCache;
|
|
433
449
|
const raw = process.env[CONTENT_CAPTURE_ENV];
|
|
@@ -436,15 +452,21 @@ function readContentCaptureEnv(): ResolvedTelemetryContentCapture {
|
|
|
436
452
|
return "none";
|
|
437
453
|
}
|
|
438
454
|
const normalized = raw.trim().toLowerCase();
|
|
439
|
-
if (normalized === "
|
|
455
|
+
if (normalized === "full") {
|
|
456
|
+
contentCaptureEnvCache = "full";
|
|
457
|
+
} else if (normalized === "summary" || normalized === "true" || normalized === "1" || normalized === "yes") {
|
|
440
458
|
contentCaptureEnvCache = "summary";
|
|
441
459
|
} else {
|
|
442
|
-
contentCaptureEnvCache =
|
|
443
|
-
normalized === "true" || normalized === "1" || normalized === "yes" || normalized === "full" ? "full" : "none";
|
|
460
|
+
contentCaptureEnvCache = "none";
|
|
444
461
|
}
|
|
445
462
|
return contentCaptureEnvCache;
|
|
446
463
|
}
|
|
447
464
|
|
|
465
|
+
export function resetContentCaptureEnvCacheForTest(): void {
|
|
466
|
+
contentCaptureEnvCache = undefined;
|
|
467
|
+
hasWarnedFullContentCaptureEnv = false;
|
|
468
|
+
}
|
|
469
|
+
|
|
448
470
|
function resolveContentCapture(value: TelemetryContentCapture | undefined): ResolvedTelemetryContentCapture {
|
|
449
471
|
const capture = value ?? readContentCaptureEnv();
|
|
450
472
|
if (capture === true || capture === "full") return "full";
|
|
@@ -452,6 +474,15 @@ function resolveContentCapture(value: TelemetryContentCapture | undefined): Reso
|
|
|
452
474
|
return "none";
|
|
453
475
|
}
|
|
454
476
|
|
|
477
|
+
function warnIfEnvFullContentCaptureActive(telemetry: AgentTelemetry, contentCaptureFromEnv: boolean): void {
|
|
478
|
+
if (!contentCaptureFromEnv || telemetry.contentCapture !== "full" || hasWarnedFullContentCaptureEnv) return;
|
|
479
|
+
hasWarnedFullContentCaptureEnv = true;
|
|
480
|
+
emitTelemetryWarning(telemetry, {
|
|
481
|
+
code: "full_content_capture_env_active",
|
|
482
|
+
message: FULL_CONTENT_CAPTURE_ENV_WARNING,
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
|
|
455
486
|
/**
|
|
456
487
|
* Start a span with the standard attribute envelope (provider, operation,
|
|
457
488
|
* conversation, agent identity, user-supplied extras) pre-applied. Returns
|
package/src/types.ts
CHANGED
|
@@ -132,6 +132,15 @@ export interface AgentLoopConfig extends SimpleStreamOptions {
|
|
|
132
132
|
* continues with another turn.
|
|
133
133
|
*/
|
|
134
134
|
getFollowUpMessages?: () => Promise<AgentMessage[]>;
|
|
135
|
+
/**
|
|
136
|
+
* Cooperative pause checkpoint evaluated at safe loop boundaries.
|
|
137
|
+
*
|
|
138
|
+
* Called after completed tool execution has been emitted and before the loop
|
|
139
|
+
* polls steering/follow-up queues or schedules another assistant response.
|
|
140
|
+
* Returning true ends the current loop with `agent_end.stopReason === "paused"`
|
|
141
|
+
* without aborting any in-flight model or tool work.
|
|
142
|
+
*/
|
|
143
|
+
shouldPause?: () => boolean;
|
|
135
144
|
/**
|
|
136
145
|
* Hook fired right before the loop would exit.
|
|
137
146
|
*
|
|
@@ -458,6 +467,8 @@ export type AgentEvent =
|
|
|
458
467
|
| {
|
|
459
468
|
type: "agent_end";
|
|
460
469
|
messages: AgentMessage[];
|
|
470
|
+
/** Indicates whether the loop ended normally or suspended at a pause checkpoint. */
|
|
471
|
+
stopReason?: "completed" | "paused";
|
|
461
472
|
/** Present iff `AgentTelemetryConfig` was supplied on this run. */
|
|
462
473
|
telemetry?: AgentRunSummary;
|
|
463
474
|
coverage?: AgentRunCoverage;
|