|
1 | 1 | // Codex plugin module implements thread lifecycle behavior. |
| 2 | +import * as crypto from "node:crypto"; |
2 | 3 | import { |
3 | 4 | buildSkillWorkshopPromptSection, |
4 | 5 | embeddedAgentLog, |
@@ -349,8 +350,12 @@ export async function startOrResumeThread(params: { |
349 | 350 | ...params.timing, |
350 | 351 | enabled: params.timing?.enabled ?? isCodexAppServerProfilerEnabled(params.params.config), |
351 | 352 | }); |
| 353 | + const legacyDynamicToolsFingerprint = lifecycleTiming.measureSync( |
| 354 | + "legacy-dynamic-tools-fingerprint", |
| 355 | + () => legacyFingerprintDynamicTools(params.dynamicTools), |
| 356 | + ); |
352 | 357 | const dynamicToolsFingerprint = lifecycleTiming.measureSync("dynamic-tools-fingerprint", () => |
353 | | - fingerprintDynamicTools(params.dynamicTools), |
| 358 | + hashDynamicToolFingerprint(legacyDynamicToolsFingerprint), |
354 | 359 | ); |
355 | 360 | const dynamicToolsContainDeferred = flattenCodexDynamicToolFunctions(params.dynamicTools).some( |
356 | 361 | (tool) => tool.deferLoading === true, |
@@ -674,11 +679,12 @@ export async function startOrResumeThread(params: { |
674 | 679 | !areDynamicToolFingerprintsCompatible( |
675 | 680 | binding.dynamicToolsFingerprint, |
676 | 681 | dynamicToolsFingerprint, |
| 682 | + legacyDynamicToolsFingerprint, |
677 | 683 | ) |
678 | 684 | ) { |
679 | 685 | preserveExistingBinding = shouldStartTransientNoToolThread({ |
680 | 686 | previous: binding.dynamicToolsFingerprint, |
681 | | - next: dynamicToolsFingerprint, |
| 687 | + nextHasDynamicTools: params.dynamicTools.length > 0, |
682 | 688 | }); |
683 | 689 | if (preserveExistingBinding) { |
684 | 690 | embeddedAgentLog.debug( |
@@ -1663,19 +1669,32 @@ export function codexDynamicToolsFingerprint(dynamicTools: CodexDynamicToolSpec[ |
1663 | 1669 | return fingerprintDynamicTools(dynamicTools); |
1664 | 1670 | } |
1665 | 1671 |
|
| 1672 | +export function codexLegacyDynamicToolsFingerprint(dynamicTools: CodexDynamicToolSpec[]): string { |
| 1673 | + return legacyFingerprintDynamicTools(dynamicTools); |
| 1674 | +} |
| 1675 | + |
1666 | 1676 | export function areCodexDynamicToolFingerprintsCompatible(params: { |
1667 | 1677 | previous?: string; |
1668 | 1678 | next: string; |
| 1679 | + nextLegacy?: string; |
1669 | 1680 | }): boolean { |
1670 | | - return areDynamicToolFingerprintsCompatible(params.previous, params.next); |
| 1681 | + return areDynamicToolFingerprintsCompatible(params.previous, params.next, params.nextLegacy); |
1671 | 1682 | } |
1672 | 1683 |
|
1673 | 1684 | function fingerprintDynamicTools(dynamicTools: CodexDynamicToolSpec[]): string { |
| 1685 | + return hashDynamicToolFingerprint(legacyFingerprintDynamicTools(dynamicTools)); |
| 1686 | +} |
| 1687 | + |
| 1688 | +function legacyFingerprintDynamicTools(dynamicTools: CodexDynamicToolSpec[]): string { |
1674 | 1689 | return JSON.stringify( |
1675 | 1690 | dynamicTools.map(fingerprintDynamicToolSpec).toSorted(compareJsonFingerprint), |
1676 | 1691 | ); |
1677 | 1692 | } |
1678 | 1693 |
|
| 1694 | +function hashDynamicToolFingerprint(canonical: string): string { |
| 1695 | + return "sha256:" + crypto.createHash("sha256").update(canonical).digest("hex"); |
| 1696 | +} |
| 1697 | + |
1679 | 1698 | function fingerprintUserMcpServersConfigPatch( |
1680 | 1699 | configPatch: JsonObject | undefined, |
1681 | 1700 | ): string | undefined { |
@@ -1740,20 +1759,34 @@ function readActiveCodexTurnIds(thread: unknown): string[] { |
1740 | 1759 | .filter((turnId) => turnId.trim().length > 0); |
1741 | 1760 | } |
1742 | 1761 |
|
1743 | | -const EMPTY_DYNAMIC_TOOLS_FINGERPRINT = JSON.stringify([]); |
| 1762 | +const LEGACY_EMPTY_DYNAMIC_TOOLS_FINGERPRINT = legacyFingerprintDynamicTools([]); |
| 1763 | +const EMPTY_DYNAMIC_TOOLS_FINGERPRINT = hashDynamicToolFingerprint( |
| 1764 | + LEGACY_EMPTY_DYNAMIC_TOOLS_FINGERPRINT, |
| 1765 | +); |
1744 | 1766 |
|
1745 | | -function areDynamicToolFingerprintsCompatible(previous: string | undefined, next: string): boolean { |
1746 | | - return !previous || previous === next; |
| 1767 | +function areDynamicToolFingerprintsCompatible( |
| 1768 | + previous: string | undefined, |
| 1769 | + next: string, |
| 1770 | + nextLegacy?: string, |
| 1771 | +): boolean { |
| 1772 | + return !previous || previous === next || previous === nextLegacy; |
1747 | 1773 | } |
1748 | 1774 |
|
1749 | 1775 | function shouldStartTransientNoToolThread(params: { |
1750 | 1776 | previous: string | undefined; |
1751 | | - next: string; |
| 1777 | + nextHasDynamicTools: boolean; |
1752 | 1778 | }): boolean { |
1753 | 1779 | return Boolean( |
1754 | 1780 | params.previous && |
1755 | | - params.previous !== EMPTY_DYNAMIC_TOOLS_FINGERPRINT && |
1756 | | - params.next === EMPTY_DYNAMIC_TOOLS_FINGERPRINT, |
| 1781 | + !isEmptyDynamicToolsFingerprint(params.previous) && |
| 1782 | + !params.nextHasDynamicTools, |
| 1783 | + ); |
| 1784 | +} |
| 1785 | + |
| 1786 | +function isEmptyDynamicToolsFingerprint(fingerprint: string): boolean { |
| 1787 | + return ( |
| 1788 | + fingerprint === EMPTY_DYNAMIC_TOOLS_FINGERPRINT || |
| 1789 | + fingerprint === LEGACY_EMPTY_DYNAMIC_TOOLS_FINGERPRINT |
1757 | 1790 | ); |
1758 | 1791 | } |
1759 | 1792 |
|
|
0 commit comments