@hiper2d/ai-agents 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -1092,8 +1092,20 @@ declare class AgentFactory {
1092
1092
  private static validateLlmTypeAndGet;
1093
1093
  }
1094
1094
 
1095
+ type CacheTtl = '5m' | '1h';
1095
1096
  declare class ClaudeAgent extends AbstractAgent {
1096
1097
  private readonly client;
1098
+ /**
1099
+ * TTL for every breakpoint this agent places (system tiers and the message anchor).
1100
+ * Anthropic bills a 5m write at 1.25x input, a 1h write at 2x, reads at 0.1x, and a read
1101
+ * refreshes the timer on either TTL. Default '1h' because the main consumer runs at human
1102
+ * pace: consecutive calls for one agent measured 12-78 minutes apart, so 5m entries
1103
+ * expired before they were ever read (0-16% hit rate over 30 days, hits only on gaps
1104
+ * under five minutes). Set '5m' for continuous traffic where every call lands inside the
1105
+ * window; there the cheaper write wins. One knob for all breakpoints on purpose: Anthropic
1106
+ * requires 1h entries to precede 5m ones, and a single TTL keeps that trivially true.
1107
+ */
1108
+ cacheTtl: CacheTtl;
1097
1109
  private get defaultParams();
1098
1110
  private readonly logTemplates;
1099
1111
  private readonly errorMessages;
@@ -1150,6 +1162,7 @@ declare class ClaudeAgent extends AbstractAgent {
1150
1162
 
1151
1163
  declare class Gpt5Agent extends AbstractAgent {
1152
1164
  private readonly client;
1165
+ private readonly promptCacheKey;
1153
1166
  private readonly logTemplates;
1154
1167
  private readonly errorMessages;
1155
1168
  constructor(name: string, instruction: string, model: string, apiKey: string, temperature: number, enableThinking?: boolean, agentLoggingConfig?: AgentLoggingConfig);
package/dist/index.d.ts CHANGED
@@ -1092,8 +1092,20 @@ declare class AgentFactory {
1092
1092
  private static validateLlmTypeAndGet;
1093
1093
  }
1094
1094
 
1095
+ type CacheTtl = '5m' | '1h';
1095
1096
  declare class ClaudeAgent extends AbstractAgent {
1096
1097
  private readonly client;
1098
+ /**
1099
+ * TTL for every breakpoint this agent places (system tiers and the message anchor).
1100
+ * Anthropic bills a 5m write at 1.25x input, a 1h write at 2x, reads at 0.1x, and a read
1101
+ * refreshes the timer on either TTL. Default '1h' because the main consumer runs at human
1102
+ * pace: consecutive calls for one agent measured 12-78 minutes apart, so 5m entries
1103
+ * expired before they were ever read (0-16% hit rate over 30 days, hits only on gaps
1104
+ * under five minutes). Set '5m' for continuous traffic where every call lands inside the
1105
+ * window; there the cheaper write wins. One knob for all breakpoints on purpose: Anthropic
1106
+ * requires 1h entries to precede 5m ones, and a single TTL keeps that trivially true.
1107
+ */
1108
+ cacheTtl: CacheTtl;
1097
1109
  private get defaultParams();
1098
1110
  private readonly logTemplates;
1099
1111
  private readonly errorMessages;
@@ -1150,6 +1162,7 @@ declare class ClaudeAgent extends AbstractAgent {
1150
1162
 
1151
1163
  declare class Gpt5Agent extends AbstractAgent {
1152
1164
  private readonly client;
1165
+ private readonly promptCacheKey;
1153
1166
  private readonly logTemplates;
1154
1167
  private readonly errorMessages;
1155
1168
  constructor(name: string, instruction: string, model: string, apiKey: string, temperature: number, enableThinking?: boolean, agentLoggingConfig?: AgentLoggingConfig);
package/dist/index.js CHANGED
@@ -2176,6 +2176,11 @@ var import_openai3 = __toESM(require("openai"));
2176
2176
  var import_zod2 = require("openai/helpers/zod");
2177
2177
  var Gpt5Agent = class extends AbstractAgent {
2178
2178
  client;
2179
+ // Routing hint for OpenAI's prefix cache (same scheme as the Mistral/Grok agents): one
2180
+ // key per agent+instruction, so an agent's own calls group together instead of every
2181
+ // agent that shares a static prefix hashing to the same route. Keys influence routing
2182
+ // only; they do not guarantee a hit.
2183
+ promptCacheKey;
2179
2184
  // Log message templates
2180
2185
  logTemplates = {
2181
2186
  error: (name, error) => `Error in ${name} agent: ${error}`
@@ -2188,6 +2193,8 @@ var Gpt5Agent = class extends AbstractAgent {
2188
2193
  };
2189
2194
  constructor(name, instruction, model, apiKey, temperature, enableThinking = false, agentLoggingConfig = DEFAULT_LOGGING_CONFIG.agents) {
2190
2195
  super(name, instruction, model, temperature, enableThinking, agentLoggingConfig);
2196
+ this.promptCacheKey = stableHashHex(`${name}
2197
+ ${instruction}`);
2191
2198
  this.client = new import_openai3.default({
2192
2199
  apiKey
2193
2200
  });
@@ -2202,10 +2209,7 @@ var Gpt5Agent = class extends AbstractAgent {
2202
2209
  try {
2203
2210
  this.logAsking(messages);
2204
2211
  this.logMessages(messages);
2205
- const input = [
2206
- `System: ${this.instruction}`,
2207
- ...this.prepareMessages(messages).map((msg) => `${msg.role === "user" ? "User" : "Assistant"}: ${msg.content}`)
2208
- ].join("\n\n");
2212
+ const input = this.prepareMessages(messages).map((msg) => `${msg.role === "user" ? "User" : "Assistant"}: ${msg.content}`).join("\n\n");
2209
2213
  const schemaToSend = zodSchema;
2210
2214
  let response;
2211
2215
  try {
@@ -2214,6 +2218,7 @@ var Gpt5Agent = class extends AbstractAgent {
2214
2218
  instructions: this.instruction,
2215
2219
  input,
2216
2220
  max_output_tokens: this.maxOutputTokens,
2221
+ prompt_cache_key: this.promptCacheKey,
2217
2222
  text: {
2218
2223
  format: (0, import_zod2.zodTextFormat)(schemaToSend, "response_schema")
2219
2224
  }
@@ -2292,15 +2297,13 @@ var Gpt5Agent = class extends AbstractAgent {
2292
2297
  try {
2293
2298
  this.logAsking(messages);
2294
2299
  this.logMessages(messages);
2295
- const input = [
2296
- `System: ${this.instruction}`,
2297
- ...this.prepareMessages(messages).map((msg) => `${msg.role === "user" ? "User" : "Assistant"}: ${msg.content}`)
2298
- ].join("\n\n");
2300
+ const input = this.prepareMessages(messages).map((msg) => `${msg.role === "user" ? "User" : "Assistant"}: ${msg.content}`).join("\n\n");
2299
2301
  const response = await this.client.responses.create({
2300
2302
  model: this.model,
2301
2303
  instructions: this.instruction,
2302
2304
  input,
2303
- max_output_tokens: this.maxOutputTokens
2305
+ max_output_tokens: this.maxOutputTokens,
2306
+ prompt_cache_key: this.promptCacheKey
2304
2307
  });
2305
2308
  const content = response.output_text;
2306
2309
  if (!content) {
@@ -2356,13 +2359,24 @@ var Gpt5Agent = class extends AbstractAgent {
2356
2359
  var import_sdk = require("@anthropic-ai/sdk");
2357
2360
  var ClaudeAgent = class extends AbstractAgent {
2358
2361
  client;
2362
+ /**
2363
+ * TTL for every breakpoint this agent places (system tiers and the message anchor).
2364
+ * Anthropic bills a 5m write at 1.25x input, a 1h write at 2x, reads at 0.1x, and a read
2365
+ * refreshes the timer on either TTL. Default '1h' because the main consumer runs at human
2366
+ * pace: consecutive calls for one agent measured 12-78 minutes apart, so 5m entries
2367
+ * expired before they were ever read (0-16% hit rate over 30 days, hits only on gaps
2368
+ * under five minutes). Set '5m' for continuous traffic where every call lands inside the
2369
+ * window; there the cheaper write wins. One knob for all breakpoints on purpose: Anthropic
2370
+ * requires 1h entries to precede 5m ones, and a single TTL keeps that trivially true.
2371
+ */
2372
+ cacheTtl = "1h";
2359
2373
  // System-prompt breakpoints, one per cache tier (see CACHE_TIER_MARKER):
2360
- // block 1 shared static rules, byte-identical across all bots and games with the
2361
- // same rule set, so one org-level entry serves everyone and ANY bot's call
2362
- // refreshes its TTL;
2363
- // block 2 per-bot identity + game state + summaries, byte-stable from the start of
2364
- // a game day through the end of its night (deaths/role knowledge/summaries
2365
- // only change in startNewDay), so every call within a day reads it.
2374
+ // block 1 - shared static rules, byte-identical across all bots and games with the
2375
+ // same rule set. Caches are scoped per model, so one entry serves every bot
2376
+ // ON THAT MODEL (not the whole lobby), and any of their calls refreshes it;
2377
+ // block 2 - per-bot identity + game state + summaries, byte-stable between the game's
2378
+ // state writes (a lynch, the night resolution, the summary rewrite, the new
2379
+ // day), so every call inside one of those windows reads it.
2366
2380
  // GM prompts have no marker → single block, same behavior as before. Haiku 4.5 needs a
2367
2381
  // 4096-token cacheable prefix, so tiers below that silently no-op on Haiku — expected.
2368
2382
  // A getter, not a field: `maxOutputTokens` can be raised after construction, and a field
@@ -2370,7 +2384,7 @@ var ClaudeAgent = class extends AbstractAgent {
2370
2384
  get defaultParams() {
2371
2385
  return {
2372
2386
  max_tokens: this.maxOutputTokens,
2373
- system: this.instructionParts.map((part) => ({ type: "text", text: part, cache_control: { type: "ephemeral" } })),
2387
+ system: this.instructionParts.map((part) => ({ type: "text", text: part, cache_control: { type: "ephemeral", ttl: this.cacheTtl } })),
2374
2388
  model: this.model
2375
2389
  };
2376
2390
  }
@@ -2475,14 +2489,14 @@ var ClaudeAgent = class extends AbstractAgent {
2475
2489
  const anchor = messages[messages.length - 2];
2476
2490
  if (typeof anchor.content === "string") {
2477
2491
  if (anchor.content.length > 0) {
2478
- anchor.content = [{ type: "text", text: anchor.content, cache_control: { type: "ephemeral" } }];
2492
+ anchor.content = [{ type: "text", text: anchor.content, cache_control: { type: "ephemeral", ttl: this.cacheTtl } }];
2479
2493
  }
2480
2494
  return;
2481
2495
  }
2482
2496
  for (let i = anchor.content.length - 1; i >= 0; i--) {
2483
2497
  const block = anchor.content[i];
2484
2498
  if (block.type === "text" && block.text.length > 0) {
2485
- block.cache_control = { type: "ephemeral" };
2499
+ block.cache_control = { type: "ephemeral", ttl: this.cacheTtl };
2486
2500
  return;
2487
2501
  }
2488
2502
  }