@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.d.mts
CHANGED
|
@@ -16,6 +16,7 @@ interface AIMessage {
|
|
|
16
16
|
googleThoughtSignature?: string;
|
|
17
17
|
grokEncryptedReasoning?: string;
|
|
18
18
|
metaEncryptedReasoning?: string;
|
|
19
|
+
openaiEncryptedReasoning?: string;
|
|
19
20
|
}
|
|
20
21
|
interface TokenUsage$1 {
|
|
21
22
|
inputTokens: number;
|
|
@@ -510,6 +511,7 @@ declare function getProviderSignatureFields(aiType: string, signature?: string):
|
|
|
510
511
|
googleThoughtSignature?: string;
|
|
511
512
|
grokEncryptedReasoning?: string;
|
|
512
513
|
metaEncryptedReasoning?: string;
|
|
514
|
+
openaiEncryptedReasoning?: string;
|
|
513
515
|
};
|
|
514
516
|
|
|
515
517
|
/**
|
|
@@ -1407,6 +1409,24 @@ declare class Gpt5Agent extends AbstractAgent {
|
|
|
1407
1409
|
* inherited from a default OpenAI can change under us.
|
|
1408
1410
|
*/
|
|
1409
1411
|
private reasoningParams;
|
|
1412
|
+
/**
|
|
1413
|
+
* Responses API input items, with each assistant turn's stored reasoning items replayed
|
|
1414
|
+
* immediately before it. OpenAI attaches `encrypted_content` to reasoning items whenever
|
|
1415
|
+
* `store` is false, which is how we call it: the documented stateless route is to preserve
|
|
1416
|
+
* the output items and replay the history yourself, rather than lean on
|
|
1417
|
+
* `previous_response_id` and OpenAI-side session state.
|
|
1418
|
+
*
|
|
1419
|
+
* This was a single flattened string before ("User: ... / Assistant: ..."), which left
|
|
1420
|
+
* nowhere to put reasoning items. The system prompt still travels ONLY as `instructions`.
|
|
1421
|
+
*
|
|
1422
|
+
* Reasoning is readable only within one model family and the API silently omits items it
|
|
1423
|
+
* cannot read, so a bot moved between GPT-6 and GPT-5.6 Terra loses its reasoning and keeps
|
|
1424
|
+
* working. A bot moved to another provider never reaches this code at all: every agent
|
|
1425
|
+
* reads only its own field, and a missing one falls back to a plain message.
|
|
1426
|
+
*/
|
|
1427
|
+
private buildResponsesInput;
|
|
1428
|
+
/** The response's reasoning items, JSON-serialized for the caller to store on the message. */
|
|
1429
|
+
private extractReasoningItems;
|
|
1410
1430
|
/**
|
|
1411
1431
|
* Structured output method using Zod with OpenAI's Responses API
|
|
1412
1432
|
* This provides better schema handling and runtime validation
|
|
@@ -1725,6 +1745,13 @@ declare class QwenAgent extends AbstractAgent {
|
|
|
1725
1745
|
* this agent stays ignored — deliberately, not because effort is broken.
|
|
1726
1746
|
*/
|
|
1727
1747
|
private thinkingParams;
|
|
1748
|
+
/**
|
|
1749
|
+
* Assistant turns carry their own `reasoning_content` back so the model can reference what
|
|
1750
|
+
* it was thinking on earlier turns (paired with `preserve_thinking` in thinkingParams).
|
|
1751
|
+
* Qwen's reasoning is PLAIN TEXT, not a signed or encrypted blob, so there is nothing for
|
|
1752
|
+
* another provider to reject: a bot whose model changes mid-game simply stops sending it
|
|
1753
|
+
* and keeps working. The text is the same one the caller already stores as `thinking`.
|
|
1754
|
+
*/
|
|
1728
1755
|
private convertToOpenAIMessages;
|
|
1729
1756
|
private extractThinkingAndUsage;
|
|
1730
1757
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ interface AIMessage {
|
|
|
16
16
|
googleThoughtSignature?: string;
|
|
17
17
|
grokEncryptedReasoning?: string;
|
|
18
18
|
metaEncryptedReasoning?: string;
|
|
19
|
+
openaiEncryptedReasoning?: string;
|
|
19
20
|
}
|
|
20
21
|
interface TokenUsage$1 {
|
|
21
22
|
inputTokens: number;
|
|
@@ -510,6 +511,7 @@ declare function getProviderSignatureFields(aiType: string, signature?: string):
|
|
|
510
511
|
googleThoughtSignature?: string;
|
|
511
512
|
grokEncryptedReasoning?: string;
|
|
512
513
|
metaEncryptedReasoning?: string;
|
|
514
|
+
openaiEncryptedReasoning?: string;
|
|
513
515
|
};
|
|
514
516
|
|
|
515
517
|
/**
|
|
@@ -1407,6 +1409,24 @@ declare class Gpt5Agent extends AbstractAgent {
|
|
|
1407
1409
|
* inherited from a default OpenAI can change under us.
|
|
1408
1410
|
*/
|
|
1409
1411
|
private reasoningParams;
|
|
1412
|
+
/**
|
|
1413
|
+
* Responses API input items, with each assistant turn's stored reasoning items replayed
|
|
1414
|
+
* immediately before it. OpenAI attaches `encrypted_content` to reasoning items whenever
|
|
1415
|
+
* `store` is false, which is how we call it: the documented stateless route is to preserve
|
|
1416
|
+
* the output items and replay the history yourself, rather than lean on
|
|
1417
|
+
* `previous_response_id` and OpenAI-side session state.
|
|
1418
|
+
*
|
|
1419
|
+
* This was a single flattened string before ("User: ... / Assistant: ..."), which left
|
|
1420
|
+
* nowhere to put reasoning items. The system prompt still travels ONLY as `instructions`.
|
|
1421
|
+
*
|
|
1422
|
+
* Reasoning is readable only within one model family and the API silently omits items it
|
|
1423
|
+
* cannot read, so a bot moved between GPT-6 and GPT-5.6 Terra loses its reasoning and keeps
|
|
1424
|
+
* working. A bot moved to another provider never reaches this code at all: every agent
|
|
1425
|
+
* reads only its own field, and a missing one falls back to a plain message.
|
|
1426
|
+
*/
|
|
1427
|
+
private buildResponsesInput;
|
|
1428
|
+
/** The response's reasoning items, JSON-serialized for the caller to store on the message. */
|
|
1429
|
+
private extractReasoningItems;
|
|
1410
1430
|
/**
|
|
1411
1431
|
* Structured output method using Zod with OpenAI's Responses API
|
|
1412
1432
|
* This provides better schema handling and runtime validation
|
|
@@ -1725,6 +1745,13 @@ declare class QwenAgent extends AbstractAgent {
|
|
|
1725
1745
|
* this agent stays ignored — deliberately, not because effort is broken.
|
|
1726
1746
|
*/
|
|
1727
1747
|
private thinkingParams;
|
|
1748
|
+
/**
|
|
1749
|
+
* Assistant turns carry their own `reasoning_content` back so the model can reference what
|
|
1750
|
+
* it was thinking on earlier turns (paired with `preserve_thinking` in thinkingParams).
|
|
1751
|
+
* Qwen's reasoning is PLAIN TEXT, not a signed or encrypted blob, so there is nothing for
|
|
1752
|
+
* another provider to reject: a bot whose model changes mid-game simply stops sending it
|
|
1753
|
+
* and keeps working. The text is the same one the caller already stores as `thinking`.
|
|
1754
|
+
*/
|
|
1728
1755
|
private convertToOpenAIMessages;
|
|
1729
1756
|
private extractThinkingAndUsage;
|
|
1730
1757
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1240,10 +1240,15 @@ var SupportedAiModels = {
|
|
|
1240
1240
|
// Live 2026-08-30 (one sample each): day-2 vote 11.8s, 15-character story 56.2s.
|
|
1241
1241
|
tags: ["cheap"]
|
|
1242
1242
|
},
|
|
1243
|
-
// Sakana Fugu models — OpenAI-compatible. They reason internally
|
|
1244
|
-
//
|
|
1245
|
-
//
|
|
1246
|
-
//
|
|
1243
|
+
// Sakana Fugu models — OpenAI-compatible. They reason internally, but Chat Completions
|
|
1244
|
+
// (what FuguAgent uses) never returns the text: reasoning_tokens are reported and billed,
|
|
1245
|
+
// there is no reasoning_content. So hasThinking is false. Single entry per model.
|
|
1246
|
+
// The Messages and Responses APIs DO return reasoning (thinking blocks with an empty
|
|
1247
|
+
// signature / reasoning items, some with encrypted_content), but probed 2026-09-24 on
|
|
1248
|
+
// fugu-max it isn't worth switching for, since we keep reasoning only to replay it: the
|
|
1249
|
+
// returned text is often a vague status summary or empty rather than the actual
|
|
1250
|
+
// reasoning, and replayed reasoning reaches the model distorted (a planted "4817" came
|
|
1251
|
+
// back as "4829" on both APIs, presumably rewritten by the orchestrator).
|
|
1247
1252
|
//
|
|
1248
1253
|
// Base `fugu` was RETIRED 2026-08-04. It was carried as a cheap everyday option at an
|
|
1249
1254
|
// assumed $1/$3, but reconciling token logs against the Sakana balance showed it actually
|
|
@@ -1702,6 +1707,9 @@ function getProviderSignatureFields(aiType, signature) {
|
|
|
1702
1707
|
if (aiType.startsWith("grok")) {
|
|
1703
1708
|
return { grokEncryptedReasoning: signature };
|
|
1704
1709
|
}
|
|
1710
|
+
if (aiType.startsWith("gpt")) {
|
|
1711
|
+
return { openaiEncryptedReasoning: signature };
|
|
1712
|
+
}
|
|
1705
1713
|
if (aiType.startsWith("muse-")) {
|
|
1706
1714
|
return { metaEncryptedReasoning: signature };
|
|
1707
1715
|
}
|
|
@@ -2526,7 +2534,54 @@ ${instruction}`);
|
|
|
2526
2534
|
* inherited from a default OpenAI can change under us.
|
|
2527
2535
|
*/
|
|
2528
2536
|
reasoningParams() {
|
|
2529
|
-
return
|
|
2537
|
+
return {
|
|
2538
|
+
reasoning: {
|
|
2539
|
+
...this.reasoningEffort ? { effort: toOpenAIEffort(this.reasoningEffort) } : {},
|
|
2540
|
+
// Render reasoning from earlier turns into this sample. It is already the
|
|
2541
|
+
// default on all four models we run, but stated explicitly for the same reason
|
|
2542
|
+
// effort is. It only does anything because we replay the items ourselves.
|
|
2543
|
+
context: "all_turns"
|
|
2544
|
+
}
|
|
2545
|
+
};
|
|
2546
|
+
}
|
|
2547
|
+
/**
|
|
2548
|
+
* Responses API input items, with each assistant turn's stored reasoning items replayed
|
|
2549
|
+
* immediately before it. OpenAI attaches `encrypted_content` to reasoning items whenever
|
|
2550
|
+
* `store` is false, which is how we call it: the documented stateless route is to preserve
|
|
2551
|
+
* the output items and replay the history yourself, rather than lean on
|
|
2552
|
+
* `previous_response_id` and OpenAI-side session state.
|
|
2553
|
+
*
|
|
2554
|
+
* This was a single flattened string before ("User: ... / Assistant: ..."), which left
|
|
2555
|
+
* nowhere to put reasoning items. The system prompt still travels ONLY as `instructions`.
|
|
2556
|
+
*
|
|
2557
|
+
* Reasoning is readable only within one model family and the API silently omits items it
|
|
2558
|
+
* cannot read, so a bot moved between GPT-6 and GPT-5.6 Terra loses its reasoning and keeps
|
|
2559
|
+
* working. A bot moved to another provider never reaches this code at all: every agent
|
|
2560
|
+
* reads only its own field, and a missing one falls back to a plain message.
|
|
2561
|
+
*/
|
|
2562
|
+
buildResponsesInput(messages) {
|
|
2563
|
+
const input = [];
|
|
2564
|
+
for (const msg of this.prepareMessages(messages)) {
|
|
2565
|
+
if (msg.role === "assistant" && msg.openaiEncryptedReasoning) {
|
|
2566
|
+
try {
|
|
2567
|
+
const items = JSON.parse(msg.openaiEncryptedReasoning);
|
|
2568
|
+
if (Array.isArray(items)) {
|
|
2569
|
+
input.push(...items);
|
|
2570
|
+
}
|
|
2571
|
+
} catch {
|
|
2572
|
+
this.logger("Failed to parse stored encrypted reasoning, replaying message without it");
|
|
2573
|
+
}
|
|
2574
|
+
}
|
|
2575
|
+
input.push({ role: msg.role, content: msg.content });
|
|
2576
|
+
}
|
|
2577
|
+
return input;
|
|
2578
|
+
}
|
|
2579
|
+
/** The response's reasoning items, JSON-serialized for the caller to store on the message. */
|
|
2580
|
+
extractReasoningItems(response) {
|
|
2581
|
+
const items = (response?.output ?? []).filter(
|
|
2582
|
+
(item) => item?.type === "reasoning" && item.encrypted_content
|
|
2583
|
+
);
|
|
2584
|
+
return items.length > 0 ? JSON.stringify(items) : void 0;
|
|
2530
2585
|
}
|
|
2531
2586
|
/**
|
|
2532
2587
|
* Structured output method using Zod with OpenAI's Responses API
|
|
@@ -2538,7 +2593,7 @@ ${instruction}`);
|
|
|
2538
2593
|
try {
|
|
2539
2594
|
this.logAsking(messages);
|
|
2540
2595
|
this.logMessages(messages);
|
|
2541
|
-
const input = this.
|
|
2596
|
+
const input = this.buildResponsesInput(messages);
|
|
2542
2597
|
const schemaToSend = zodSchema;
|
|
2543
2598
|
let response;
|
|
2544
2599
|
try {
|
|
@@ -2548,6 +2603,8 @@ ${instruction}`);
|
|
|
2548
2603
|
input,
|
|
2549
2604
|
max_output_tokens: this.maxOutputTokens,
|
|
2550
2605
|
prompt_cache_key: this.promptCacheKey,
|
|
2606
|
+
// Stateless: encrypted reasoning is only returned for unstored responses.
|
|
2607
|
+
store: false,
|
|
2551
2608
|
...this.reasoningParams(),
|
|
2552
2609
|
text: {
|
|
2553
2610
|
format: (0, import_zod2.zodTextFormat)(schemaToSend, "response_schema")
|
|
@@ -2608,7 +2665,7 @@ ${instruction}`);
|
|
|
2608
2665
|
this.logReply(response.output_parsed, reasoningContent, tokenUsage);
|
|
2609
2666
|
}
|
|
2610
2667
|
this.logger(`\u2705 Response validated successfully with Zod schema`);
|
|
2611
|
-
return [response.output_parsed, reasoningContent, tokenUsage];
|
|
2668
|
+
return [response.output_parsed, reasoningContent, tokenUsage, this.extractReasoningItems(response)];
|
|
2612
2669
|
} catch (error) {
|
|
2613
2670
|
this.logger(this.logTemplates.error(this.name, error));
|
|
2614
2671
|
if (error instanceof ModelError) {
|
|
@@ -2627,13 +2684,14 @@ ${instruction}`);
|
|
|
2627
2684
|
try {
|
|
2628
2685
|
this.logAsking(messages);
|
|
2629
2686
|
this.logMessages(messages);
|
|
2630
|
-
const input = this.
|
|
2687
|
+
const input = this.buildResponsesInput(messages);
|
|
2631
2688
|
const response = await this.client.responses.create({
|
|
2632
2689
|
model: this.model,
|
|
2633
2690
|
instructions: this.instruction,
|
|
2634
2691
|
input,
|
|
2635
2692
|
max_output_tokens: this.maxOutputTokens,
|
|
2636
2693
|
prompt_cache_key: this.promptCacheKey,
|
|
2694
|
+
store: false,
|
|
2637
2695
|
...this.reasoningParams()
|
|
2638
2696
|
});
|
|
2639
2697
|
const content = response.output_text;
|
|
@@ -2675,7 +2733,7 @@ ${instruction}`);
|
|
|
2675
2733
|
}
|
|
2676
2734
|
}
|
|
2677
2735
|
this.logReply(content, "", tokenUsage);
|
|
2678
|
-
return [content, "", tokenUsage];
|
|
2736
|
+
return [content, "", tokenUsage, this.extractReasoningItems(response)];
|
|
2679
2737
|
} catch (error) {
|
|
2680
2738
|
this.logger(this.logTemplates.error(this.name, error));
|
|
2681
2739
|
if (error instanceof ModelError) {
|
|
@@ -4767,13 +4825,29 @@ var QwenAgent = class extends AbstractAgent {
|
|
|
4767
4825
|
const budget = this.thinkingBudgetTokens;
|
|
4768
4826
|
return {
|
|
4769
4827
|
enable_thinking: this.enableThinking,
|
|
4770
|
-
...this.enableThinking && budget !== void 0 ? { thinking_budget: budget } : {}
|
|
4828
|
+
...this.enableThinking && budget !== void 0 ? { thinking_budget: budget } : {},
|
|
4829
|
+
// Lets the model read the `reasoning_content` we replay on assistant messages, so a
|
|
4830
|
+
// bot keeps its own train of thought across turns. Qwen documents models as NOT
|
|
4831
|
+
// reading replayed reasoning unless this is set (qwen3.8-max and qwen3.8-flash are
|
|
4832
|
+
// both on the supported list). Measured 2026-09-22: qwen3.8-max actually used the
|
|
4833
|
+
// replayed reasoning with the flag off too, but we follow the documented contract
|
|
4834
|
+
// rather than rely on that. Stateless — the reasoning comes from our payload, not
|
|
4835
|
+
// from any server-side session.
|
|
4836
|
+
...this.enableThinking ? { preserve_thinking: true } : {}
|
|
4771
4837
|
};
|
|
4772
4838
|
}
|
|
4839
|
+
/**
|
|
4840
|
+
* Assistant turns carry their own `reasoning_content` back so the model can reference what
|
|
4841
|
+
* it was thinking on earlier turns (paired with `preserve_thinking` in thinkingParams).
|
|
4842
|
+
* Qwen's reasoning is PLAIN TEXT, not a signed or encrypted blob, so there is nothing for
|
|
4843
|
+
* another provider to reject: a bot whose model changes mid-game simply stops sending it
|
|
4844
|
+
* and keeps working. The text is the same one the caller already stores as `thinking`.
|
|
4845
|
+
*/
|
|
4773
4846
|
convertToOpenAIMessages(messages) {
|
|
4774
4847
|
return messages.map((msg) => ({
|
|
4775
4848
|
role: msg.role,
|
|
4776
|
-
content: msg.content
|
|
4849
|
+
content: msg.content,
|
|
4850
|
+
...msg.role === "assistant" && msg.thinking ? { reasoning_content: msg.thinking } : {}
|
|
4777
4851
|
}));
|
|
4778
4852
|
}
|
|
4779
4853
|
extractThinkingAndUsage(completion) {
|
|
@@ -4840,7 +4914,11 @@ ${schemaDescription}`;
|
|
|
4840
4914
|
const params = {
|
|
4841
4915
|
...this.defaultParams,
|
|
4842
4916
|
messages: openAIMessages,
|
|
4843
|
-
...this.thinkingParams()
|
|
4917
|
+
...this.thinkingParams(),
|
|
4918
|
+
response_format: {
|
|
4919
|
+
type: "json_schema",
|
|
4920
|
+
json_schema: ZodSchemaConverter.toOpenAIJsonSchema(zodSchema, "response_schema")
|
|
4921
|
+
}
|
|
4844
4922
|
};
|
|
4845
4923
|
completion = await this.client.chat.completions.create(params);
|
|
4846
4924
|
} catch (apiError) {
|