@hiper2d/ai-agents 0.13.1 → 0.15.0
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 +27 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +90 -12
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +90 -12
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1065,10 +1065,15 @@ var SupportedAiModels = {
|
|
|
1065
1065
|
// Live 2026-08-30 (one sample each): day-2 vote 11.8s, 15-character story 56.2s.
|
|
1066
1066
|
tags: ["cheap"]
|
|
1067
1067
|
},
|
|
1068
|
-
// Sakana Fugu models — OpenAI-compatible. They reason internally
|
|
1069
|
-
//
|
|
1070
|
-
//
|
|
1071
|
-
//
|
|
1068
|
+
// Sakana Fugu models — OpenAI-compatible. They reason internally, but Chat Completions
|
|
1069
|
+
// (what FuguAgent uses) never returns the text: reasoning_tokens are reported and billed,
|
|
1070
|
+
// there is no reasoning_content. So hasThinking is false. Single entry per model.
|
|
1071
|
+
// The Messages and Responses APIs DO return reasoning (thinking blocks with an empty
|
|
1072
|
+
// signature / reasoning items, some with encrypted_content), but probed 2026-09-24 on
|
|
1073
|
+
// fugu-max it isn't worth switching for, since we keep reasoning only to replay it: the
|
|
1074
|
+
// returned text is often a vague status summary or empty rather than the actual
|
|
1075
|
+
// reasoning, and replayed reasoning reaches the model distorted (a planted "4817" came
|
|
1076
|
+
// back as "4829" on both APIs, presumably rewritten by the orchestrator).
|
|
1072
1077
|
//
|
|
1073
1078
|
// Base `fugu` was RETIRED 2026-08-04. It was carried as a cheap everyday option at an
|
|
1074
1079
|
// assumed $1/$3, but reconciling token logs against the Sakana balance showed it actually
|
|
@@ -1527,6 +1532,9 @@ function getProviderSignatureFields(aiType, signature) {
|
|
|
1527
1532
|
if (aiType.startsWith("grok")) {
|
|
1528
1533
|
return { grokEncryptedReasoning: signature };
|
|
1529
1534
|
}
|
|
1535
|
+
if (aiType.startsWith("gpt")) {
|
|
1536
|
+
return { openaiEncryptedReasoning: signature };
|
|
1537
|
+
}
|
|
1530
1538
|
if (aiType.startsWith("muse-")) {
|
|
1531
1539
|
return { metaEncryptedReasoning: signature };
|
|
1532
1540
|
}
|
|
@@ -2351,7 +2359,54 @@ ${instruction}`);
|
|
|
2351
2359
|
* inherited from a default OpenAI can change under us.
|
|
2352
2360
|
*/
|
|
2353
2361
|
reasoningParams() {
|
|
2354
|
-
return
|
|
2362
|
+
return {
|
|
2363
|
+
reasoning: {
|
|
2364
|
+
...this.reasoningEffort ? { effort: toOpenAIEffort(this.reasoningEffort) } : {},
|
|
2365
|
+
// Render reasoning from earlier turns into this sample. It is already the
|
|
2366
|
+
// default on all four models we run, but stated explicitly for the same reason
|
|
2367
|
+
// effort is. It only does anything because we replay the items ourselves.
|
|
2368
|
+
context: "all_turns"
|
|
2369
|
+
}
|
|
2370
|
+
};
|
|
2371
|
+
}
|
|
2372
|
+
/**
|
|
2373
|
+
* Responses API input items, with each assistant turn's stored reasoning items replayed
|
|
2374
|
+
* immediately before it. OpenAI attaches `encrypted_content` to reasoning items whenever
|
|
2375
|
+
* `store` is false, which is how we call it: the documented stateless route is to preserve
|
|
2376
|
+
* the output items and replay the history yourself, rather than lean on
|
|
2377
|
+
* `previous_response_id` and OpenAI-side session state.
|
|
2378
|
+
*
|
|
2379
|
+
* This was a single flattened string before ("User: ... / Assistant: ..."), which left
|
|
2380
|
+
* nowhere to put reasoning items. The system prompt still travels ONLY as `instructions`.
|
|
2381
|
+
*
|
|
2382
|
+
* Reasoning is readable only within one model family and the API silently omits items it
|
|
2383
|
+
* cannot read, so a bot moved between GPT-6 and GPT-5.6 Terra loses its reasoning and keeps
|
|
2384
|
+
* working. A bot moved to another provider never reaches this code at all: every agent
|
|
2385
|
+
* reads only its own field, and a missing one falls back to a plain message.
|
|
2386
|
+
*/
|
|
2387
|
+
buildResponsesInput(messages) {
|
|
2388
|
+
const input = [];
|
|
2389
|
+
for (const msg of this.prepareMessages(messages)) {
|
|
2390
|
+
if (msg.role === "assistant" && msg.openaiEncryptedReasoning) {
|
|
2391
|
+
try {
|
|
2392
|
+
const items = JSON.parse(msg.openaiEncryptedReasoning);
|
|
2393
|
+
if (Array.isArray(items)) {
|
|
2394
|
+
input.push(...items);
|
|
2395
|
+
}
|
|
2396
|
+
} catch {
|
|
2397
|
+
this.logger("Failed to parse stored encrypted reasoning, replaying message without it");
|
|
2398
|
+
}
|
|
2399
|
+
}
|
|
2400
|
+
input.push({ role: msg.role, content: msg.content });
|
|
2401
|
+
}
|
|
2402
|
+
return input;
|
|
2403
|
+
}
|
|
2404
|
+
/** The response's reasoning items, JSON-serialized for the caller to store on the message. */
|
|
2405
|
+
extractReasoningItems(response) {
|
|
2406
|
+
const items = (response?.output ?? []).filter(
|
|
2407
|
+
(item) => item?.type === "reasoning" && item.encrypted_content
|
|
2408
|
+
);
|
|
2409
|
+
return items.length > 0 ? JSON.stringify(items) : void 0;
|
|
2355
2410
|
}
|
|
2356
2411
|
/**
|
|
2357
2412
|
* Structured output method using Zod with OpenAI's Responses API
|
|
@@ -2363,7 +2418,7 @@ ${instruction}`);
|
|
|
2363
2418
|
try {
|
|
2364
2419
|
this.logAsking(messages);
|
|
2365
2420
|
this.logMessages(messages);
|
|
2366
|
-
const input = this.
|
|
2421
|
+
const input = this.buildResponsesInput(messages);
|
|
2367
2422
|
const schemaToSend = zodSchema;
|
|
2368
2423
|
let response;
|
|
2369
2424
|
try {
|
|
@@ -2373,6 +2428,8 @@ ${instruction}`);
|
|
|
2373
2428
|
input,
|
|
2374
2429
|
max_output_tokens: this.maxOutputTokens,
|
|
2375
2430
|
prompt_cache_key: this.promptCacheKey,
|
|
2431
|
+
// Stateless: encrypted reasoning is only returned for unstored responses.
|
|
2432
|
+
store: false,
|
|
2376
2433
|
...this.reasoningParams(),
|
|
2377
2434
|
text: {
|
|
2378
2435
|
format: zodTextFormat(schemaToSend, "response_schema")
|
|
@@ -2433,7 +2490,7 @@ ${instruction}`);
|
|
|
2433
2490
|
this.logReply(response.output_parsed, reasoningContent, tokenUsage);
|
|
2434
2491
|
}
|
|
2435
2492
|
this.logger(`\u2705 Response validated successfully with Zod schema`);
|
|
2436
|
-
return [response.output_parsed, reasoningContent, tokenUsage];
|
|
2493
|
+
return [response.output_parsed, reasoningContent, tokenUsage, this.extractReasoningItems(response)];
|
|
2437
2494
|
} catch (error) {
|
|
2438
2495
|
this.logger(this.logTemplates.error(this.name, error));
|
|
2439
2496
|
if (error instanceof ModelError) {
|
|
@@ -2452,13 +2509,14 @@ ${instruction}`);
|
|
|
2452
2509
|
try {
|
|
2453
2510
|
this.logAsking(messages);
|
|
2454
2511
|
this.logMessages(messages);
|
|
2455
|
-
const input = this.
|
|
2512
|
+
const input = this.buildResponsesInput(messages);
|
|
2456
2513
|
const response = await this.client.responses.create({
|
|
2457
2514
|
model: this.model,
|
|
2458
2515
|
instructions: this.instruction,
|
|
2459
2516
|
input,
|
|
2460
2517
|
max_output_tokens: this.maxOutputTokens,
|
|
2461
2518
|
prompt_cache_key: this.promptCacheKey,
|
|
2519
|
+
store: false,
|
|
2462
2520
|
...this.reasoningParams()
|
|
2463
2521
|
});
|
|
2464
2522
|
const content = response.output_text;
|
|
@@ -2500,7 +2558,7 @@ ${instruction}`);
|
|
|
2500
2558
|
}
|
|
2501
2559
|
}
|
|
2502
2560
|
this.logReply(content, "", tokenUsage);
|
|
2503
|
-
return [content, "", tokenUsage];
|
|
2561
|
+
return [content, "", tokenUsage, this.extractReasoningItems(response)];
|
|
2504
2562
|
} catch (error) {
|
|
2505
2563
|
this.logger(this.logTemplates.error(this.name, error));
|
|
2506
2564
|
if (error instanceof ModelError) {
|
|
@@ -4592,13 +4650,29 @@ var QwenAgent = class extends AbstractAgent {
|
|
|
4592
4650
|
const budget = this.thinkingBudgetTokens;
|
|
4593
4651
|
return {
|
|
4594
4652
|
enable_thinking: this.enableThinking,
|
|
4595
|
-
...this.enableThinking && budget !== void 0 ? { thinking_budget: budget } : {}
|
|
4653
|
+
...this.enableThinking && budget !== void 0 ? { thinking_budget: budget } : {},
|
|
4654
|
+
// Lets the model read the `reasoning_content` we replay on assistant messages, so a
|
|
4655
|
+
// bot keeps its own train of thought across turns. Qwen documents models as NOT
|
|
4656
|
+
// reading replayed reasoning unless this is set (qwen3.8-max and qwen3.8-flash are
|
|
4657
|
+
// both on the supported list). Measured 2026-09-22: qwen3.8-max actually used the
|
|
4658
|
+
// replayed reasoning with the flag off too, but we follow the documented contract
|
|
4659
|
+
// rather than rely on that. Stateless — the reasoning comes from our payload, not
|
|
4660
|
+
// from any server-side session.
|
|
4661
|
+
...this.enableThinking ? { preserve_thinking: true } : {}
|
|
4596
4662
|
};
|
|
4597
4663
|
}
|
|
4664
|
+
/**
|
|
4665
|
+
* Assistant turns carry their own `reasoning_content` back so the model can reference what
|
|
4666
|
+
* it was thinking on earlier turns (paired with `preserve_thinking` in thinkingParams).
|
|
4667
|
+
* Qwen's reasoning is PLAIN TEXT, not a signed or encrypted blob, so there is nothing for
|
|
4668
|
+
* another provider to reject: a bot whose model changes mid-game simply stops sending it
|
|
4669
|
+
* and keeps working. The text is the same one the caller already stores as `thinking`.
|
|
4670
|
+
*/
|
|
4598
4671
|
convertToOpenAIMessages(messages) {
|
|
4599
4672
|
return messages.map((msg) => ({
|
|
4600
4673
|
role: msg.role,
|
|
4601
|
-
content: msg.content
|
|
4674
|
+
content: msg.content,
|
|
4675
|
+
...msg.role === "assistant" && msg.thinking ? { reasoning_content: msg.thinking } : {}
|
|
4602
4676
|
}));
|
|
4603
4677
|
}
|
|
4604
4678
|
extractThinkingAndUsage(completion) {
|
|
@@ -4665,7 +4739,11 @@ ${schemaDescription}`;
|
|
|
4665
4739
|
const params = {
|
|
4666
4740
|
...this.defaultParams,
|
|
4667
4741
|
messages: openAIMessages,
|
|
4668
|
-
...this.thinkingParams()
|
|
4742
|
+
...this.thinkingParams(),
|
|
4743
|
+
response_format: {
|
|
4744
|
+
type: "json_schema",
|
|
4745
|
+
json_schema: ZodSchemaConverter.toOpenAIJsonSchema(zodSchema, "response_schema")
|
|
4746
|
+
}
|
|
4669
4747
|
};
|
|
4670
4748
|
completion = await this.client.chat.completions.create(params);
|
|
4671
4749
|
} catch (apiError) {
|