@poncho-ai/harness 0.30.0 → 0.31.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.js CHANGED
@@ -134,7 +134,7 @@ var parseAgentMarkdown = (content) => {
134
134
  if (typeof parsed.name !== "string" || parsed.name.trim() === "") {
135
135
  throw new Error("Invalid AGENT.md: frontmatter requires a non-empty `name`.");
136
136
  }
137
- const KNOWN_PROVIDERS = /* @__PURE__ */ new Set(["anthropic", "openai"]);
137
+ const KNOWN_PROVIDERS = /* @__PURE__ */ new Set(["anthropic", "openai", "openai-codex"]);
138
138
  const splitProviderPrefix = (raw) => {
139
139
  const slashIdx = raw.indexOf("/");
140
140
  if (slashIdx > 0) {
@@ -1440,6 +1440,11 @@ All credentials in \`poncho.config.js\` use **env var name** fields (\`*Env\` su
1440
1440
  |---|---|---|
1441
1441
  | \`providers.anthropic.apiKeyEnv\` | \`ANTHROPIC_API_KEY\` | Anthropic model API key |
1442
1442
  | \`providers.openai.apiKeyEnv\` | \`OPENAI_API_KEY\` | OpenAI model API key |
1443
+ | \`providers.openaiCodex.refreshTokenEnv\` | \`OPENAI_CODEX_REFRESH_TOKEN\` | OpenAI Codex OAuth refresh token |
1444
+ | \`providers.openaiCodex.accountIdEnv\` | \`OPENAI_CODEX_ACCOUNT_ID\` | OpenAI Codex account/org routing header (optional) |
1445
+ | \`providers.openaiCodex.accessTokenEnv\` | \`OPENAI_CODEX_ACCESS_TOKEN\` | OpenAI Codex OAuth access token seed (optional) |
1446
+ | \`providers.openaiCodex.accessTokenExpiresAtEnv\` | \`OPENAI_CODEX_ACCESS_TOKEN_EXPIRES_AT\` | Access token epoch expiry in ms (optional) |
1447
+ | \`providers.openaiCodex.authFilePathEnv\` | \`OPENAI_CODEX_AUTH_FILE\` | Overrides local auth file path used by \`poncho auth\` |
1443
1448
  | \`auth.tokenEnv\` | \`PONCHO_AUTH_TOKEN\` | Auth passphrase / bearer token |
1444
1449
  | \`storage.urlEnv\` | \`UPSTASH_REDIS_REST_URL\` / \`REDIS_URL\` | Storage connection URL |
1445
1450
  | \`storage.tokenEnv\` | \`UPSTASH_REDIS_REST_TOKEN\` | Upstash REST token |
@@ -1511,6 +1516,10 @@ export default {
1511
1516
  providers: {
1512
1517
  // anthropic: { apiKeyEnv: 'ANTHROPIC_API_KEY' }, // default
1513
1518
  // openai: { apiKeyEnv: 'OPENAI_API_KEY' }, // default
1519
+ // openaiCodex: {
1520
+ // refreshTokenEnv: 'OPENAI_CODEX_REFRESH_TOKEN',
1521
+ // accountIdEnv: 'OPENAI_CODEX_ACCOUNT_ID', // optional
1522
+ // },
1514
1523
  },
1515
1524
 
1516
1525
  // Unified storage (preferred). Replaces separate \`state\` and \`memory\` blocks.
@@ -1609,6 +1618,11 @@ Remote storage keys are namespaced and versioned, for example \`poncho:v1:<agent
1609
1618
  |----------|----------|-------------|
1610
1619
  | \`ANTHROPIC_API_KEY\` | Yes* | Claude API key |
1611
1620
  | \`OPENAI_API_KEY\` | No | OpenAI API key (if using OpenAI) |
1621
+ | \`OPENAI_CODEX_REFRESH_TOKEN\` | No | OpenAI Codex OAuth refresh token (if using \`model.provider: openai-codex\`) |
1622
+ | \`OPENAI_CODEX_ACCOUNT_ID\` | No | OpenAI Codex account/org id for request routing (optional) |
1623
+ | \`OPENAI_CODEX_ACCESS_TOKEN\` | No | Optional pre-seeded short-lived access token |
1624
+ | \`OPENAI_CODEX_ACCESS_TOKEN_EXPIRES_AT\` | No | Epoch millis expiry for \`OPENAI_CODEX_ACCESS_TOKEN\` |
1625
+ | \`OPENAI_CODEX_AUTH_FILE\` | No | Local auth store override for \`poncho auth\` commands |
1612
1626
  | \`PONCHO_AUTH_TOKEN\` | No | Unified auth token (Web UI passphrase + API Bearer token) |
1613
1627
  | \`PONCHO_INTERNAL_SECRET\` | No | Shared secret used by internal serverless callbacks (recommended for Vercel/Lambda) |
1614
1628
  | \`PONCHO_SELF_BASE_URL\` | No | Explicit base URL for internal self-callbacks when auto-detection is unavailable |
@@ -1833,6 +1847,36 @@ Make sure you have a \`.env\` file with your API key:
1833
1847
  echo "ANTHROPIC_API_KEY=sk-ant-..." > .env
1834
1848
  \`\`\`
1835
1849
 
1850
+ ### "OpenAI Codex credentials not found"
1851
+
1852
+ Bootstrap credentials with device auth, then export env values:
1853
+
1854
+ \`\`\`bash
1855
+ poncho auth login --provider openai-codex --device
1856
+ poncho auth export --provider openai-codex --format env
1857
+ \`\`\`
1858
+
1859
+ For production, copy exported values into your deployment secret manager (not committed files).
1860
+
1861
+ ### "OpenAI Codex token refresh failed" / \`invalid_grant\`
1862
+
1863
+ Your refresh token is expired or rotated. Re-run one-time auth and rotate deployment secrets:
1864
+
1865
+ \`\`\`bash
1866
+ poncho auth login --provider openai-codex --device
1867
+ poncho auth export --provider openai-codex --format env
1868
+ \`\`\`
1869
+
1870
+ Then restart your deployment so new secrets are loaded.
1871
+
1872
+ ### "Missing scopes: model.request / api.model.read / api.responses.write"
1873
+
1874
+ Re-authenticate and ensure the OAuth flow requested required scopes. Then verify:
1875
+
1876
+ \`\`\`bash
1877
+ poncho auth status --provider openai-codex
1878
+ \`\`\`
1879
+
1836
1880
  ### "MCP server failed to connect"
1837
1881
 
1838
1882
  Check that:
@@ -2064,8 +2108,8 @@ var ponchoDocsTool = defineTool({
2064
2108
 
2065
2109
  // src/harness.ts
2066
2110
  import { randomUUID as randomUUID3 } from "crypto";
2067
- import { readFile as readFile8 } from "fs/promises";
2068
- import { resolve as resolve10 } from "path";
2111
+ import { readFile as readFile9 } from "fs/promises";
2112
+ import { resolve as resolve11 } from "path";
2069
2113
  import { getTextContent as getTextContent2 } from "@poncho-ai/sdk";
2070
2114
 
2071
2115
  // src/upload-store.ts
@@ -3572,6 +3616,252 @@ var LocalMcpBridge = class {
3572
3616
  // src/model-factory.ts
3573
3617
  import { createOpenAI } from "@ai-sdk/openai";
3574
3618
  import { createAnthropic } from "@ai-sdk/anthropic";
3619
+
3620
+ // src/openai-codex-auth.ts
3621
+ import { homedir as homedir3 } from "os";
3622
+ import { dirname as dirname4, resolve as resolve8 } from "path";
3623
+ import { mkdir as mkdir5, readFile as readFile7, chmod, writeFile as writeFile6, rm as rm3 } from "fs/promises";
3624
+ var OPENAI_CODEX_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
3625
+ var OPENAI_AUTH_ISSUER = "https://auth.openai.com";
3626
+ var REFRESH_TOKEN_GRACE_MS = 5 * 60 * 1e3;
3627
+ var DEVICE_POLLING_SAFETY_MARGIN_MS = 3e3;
3628
+ var DEVICE_FLOW_TIMEOUT_MS = 10 * 60 * 1e3;
3629
+ var REQUIRED_SCOPES = [
3630
+ "openid",
3631
+ "profile",
3632
+ "email",
3633
+ "offline_access",
3634
+ "api.responses.write",
3635
+ "model.request",
3636
+ "api.model.read"
3637
+ ];
3638
+ var defaultedConfig = (config) => ({
3639
+ refreshTokenEnv: config?.refreshTokenEnv ?? "OPENAI_CODEX_REFRESH_TOKEN",
3640
+ accessTokenEnv: config?.accessTokenEnv ?? "OPENAI_CODEX_ACCESS_TOKEN",
3641
+ accessTokenExpiresAtEnv: config?.accessTokenExpiresAtEnv ?? "OPENAI_CODEX_ACCESS_TOKEN_EXPIRES_AT",
3642
+ accountIdEnv: config?.accountIdEnv ?? "OPENAI_CODEX_ACCOUNT_ID",
3643
+ authFilePathEnv: config?.authFilePathEnv ?? "OPENAI_CODEX_AUTH_FILE"
3644
+ });
3645
+ var parseEpochMillis = (value) => {
3646
+ if (!value) return void 0;
3647
+ const parsed = Number.parseInt(value, 10);
3648
+ if (Number.isNaN(parsed) || parsed <= 0) return void 0;
3649
+ return parsed;
3650
+ };
3651
+ var getOpenAICodexAuthFilePath = (config) => {
3652
+ const env = defaultedConfig(config);
3653
+ const fromEnv = process.env[env.authFilePathEnv];
3654
+ if (typeof fromEnv === "string" && fromEnv.trim().length > 0) {
3655
+ return resolve8(fromEnv);
3656
+ }
3657
+ return resolve8(homedir3(), ".poncho", "auth", "openai-codex.json");
3658
+ };
3659
+ var readOpenAICodexSession = async (config) => {
3660
+ const filePath = getOpenAICodexAuthFilePath(config);
3661
+ try {
3662
+ const content = await readFile7(filePath, "utf8");
3663
+ const parsed = JSON.parse(content);
3664
+ if (typeof parsed.refreshToken !== "string" || parsed.refreshToken.length === 0) {
3665
+ return void 0;
3666
+ }
3667
+ return {
3668
+ refreshToken: parsed.refreshToken,
3669
+ accessToken: typeof parsed.accessToken === "string" && parsed.accessToken.length > 0 ? parsed.accessToken : void 0,
3670
+ accessTokenExpiresAt: typeof parsed.accessTokenExpiresAt === "number" && parsed.accessTokenExpiresAt > 0 ? parsed.accessTokenExpiresAt : void 0,
3671
+ accountId: typeof parsed.accountId === "string" && parsed.accountId.length > 0 ? parsed.accountId : void 0
3672
+ };
3673
+ } catch {
3674
+ return void 0;
3675
+ }
3676
+ };
3677
+ var writeOpenAICodexSession = async (session, config) => {
3678
+ const filePath = getOpenAICodexAuthFilePath(config);
3679
+ await mkdir5(dirname4(filePath), { recursive: true });
3680
+ const payload = {
3681
+ ...session,
3682
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
3683
+ };
3684
+ await writeFile6(filePath, `${JSON.stringify(payload, null, 2)}
3685
+ `, "utf8");
3686
+ await chmod(filePath, 384);
3687
+ };
3688
+ var deleteOpenAICodexSession = async (config) => {
3689
+ const filePath = getOpenAICodexAuthFilePath(config);
3690
+ await rm3(filePath, { force: true });
3691
+ };
3692
+ var parseAccountIdFromJwt = (token) => {
3693
+ if (!token) return void 0;
3694
+ const parts = token.split(".");
3695
+ if (parts.length !== 3) return void 0;
3696
+ try {
3697
+ const claims = JSON.parse(Buffer.from(parts[1] ?? "", "base64url").toString("utf8"));
3698
+ return claims.chatgpt_account_id ?? claims["https://api.openai.com/auth"]?.chatgpt_account_id ?? claims.organizations?.[0]?.id;
3699
+ } catch {
3700
+ return void 0;
3701
+ }
3702
+ };
3703
+ var exchangeRefreshToken = async (refreshToken) => {
3704
+ const response = await fetch(`${OPENAI_AUTH_ISSUER}/oauth/token`, {
3705
+ method: "POST",
3706
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
3707
+ body: new URLSearchParams({
3708
+ grant_type: "refresh_token",
3709
+ refresh_token: refreshToken,
3710
+ client_id: OPENAI_CODEX_CLIENT_ID
3711
+ }).toString()
3712
+ });
3713
+ if (!response.ok) {
3714
+ const body = await response.text();
3715
+ throw new Error(
3716
+ `OpenAI Codex token refresh failed (${response.status}). Re-run \`poncho auth login --provider openai-codex --device\`, export the new token, and update deployment secrets. Details: ${body.slice(0, 240)}`
3717
+ );
3718
+ }
3719
+ return await response.json();
3720
+ };
3721
+ var exchangeAuthorizationCode = async (code, codeVerifier) => {
3722
+ const response = await fetch(`${OPENAI_AUTH_ISSUER}/oauth/token`, {
3723
+ method: "POST",
3724
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
3725
+ body: new URLSearchParams({
3726
+ grant_type: "authorization_code",
3727
+ code,
3728
+ redirect_uri: `${OPENAI_AUTH_ISSUER}/deviceauth/callback`,
3729
+ client_id: OPENAI_CODEX_CLIENT_ID,
3730
+ code_verifier: codeVerifier
3731
+ }).toString()
3732
+ });
3733
+ if (!response.ok) {
3734
+ const body = await response.text();
3735
+ throw new Error(
3736
+ `OpenAI Codex device token exchange failed (${response.status}): ${body.slice(0, 240)}`
3737
+ );
3738
+ }
3739
+ return await response.json();
3740
+ };
3741
+ var readSessionFromEnv = (config) => {
3742
+ const env = defaultedConfig(config);
3743
+ const refreshToken = process.env[env.refreshTokenEnv];
3744
+ if (!refreshToken || refreshToken.trim().length === 0) return void 0;
3745
+ return {
3746
+ refreshToken: refreshToken.trim(),
3747
+ accessToken: process.env[env.accessTokenEnv]?.trim() || void 0,
3748
+ accessTokenExpiresAt: parseEpochMillis(process.env[env.accessTokenExpiresAtEnv]),
3749
+ accountId: process.env[env.accountIdEnv]?.trim() || void 0
3750
+ };
3751
+ };
3752
+ var shouldRefresh = (expiresAt) => !expiresAt || Date.now() + REFRESH_TOKEN_GRACE_MS >= expiresAt;
3753
+ var runtimeCachedSession;
3754
+ var readSession = async (config) => {
3755
+ const envSession = readSessionFromEnv(config);
3756
+ if (envSession) return { session: envSession, source: "env" };
3757
+ if (runtimeCachedSession) {
3758
+ return { session: runtimeCachedSession, source: "file" };
3759
+ }
3760
+ const fileSession = await readOpenAICodexSession(config);
3761
+ if (!fileSession) {
3762
+ throw new Error(
3763
+ "OpenAI Codex credentials not found. Run `poncho auth login --provider openai-codex --device` locally, or set OPENAI_CODEX_REFRESH_TOKEN in your environment."
3764
+ );
3765
+ }
3766
+ runtimeCachedSession = fileSession;
3767
+ return { session: fileSession, source: "file" };
3768
+ };
3769
+ var getOpenAICodexAccessToken = async (config) => {
3770
+ const { session, source } = await readSession(config);
3771
+ if (session.accessToken && !shouldRefresh(session.accessTokenExpiresAt)) {
3772
+ return { accessToken: session.accessToken, accountId: session.accountId };
3773
+ }
3774
+ const refreshed = await exchangeRefreshToken(session.refreshToken);
3775
+ const nextSession = {
3776
+ refreshToken: refreshed.refresh_token ?? session.refreshToken,
3777
+ accessToken: refreshed.access_token,
3778
+ accessTokenExpiresAt: Date.now() + (refreshed.expires_in ?? 3600) * 1e3,
3779
+ accountId: session.accountId ?? parseAccountIdFromJwt(refreshed.id_token) ?? parseAccountIdFromJwt(refreshed.access_token)
3780
+ };
3781
+ runtimeCachedSession = nextSession;
3782
+ if (source === "file") {
3783
+ await writeOpenAICodexSession(nextSession, config);
3784
+ }
3785
+ return {
3786
+ accessToken: nextSession.accessToken,
3787
+ accountId: nextSession.accountId
3788
+ };
3789
+ };
3790
+ var getOpenAICodexRequiredScopes = () => [...REQUIRED_SCOPES];
3791
+ var startOpenAICodexDeviceAuth = async () => {
3792
+ const response = await fetch(`${OPENAI_AUTH_ISSUER}/api/accounts/deviceauth/usercode`, {
3793
+ method: "POST",
3794
+ headers: {
3795
+ "Content-Type": "application/json",
3796
+ "User-Agent": "poncho/1.0"
3797
+ },
3798
+ body: JSON.stringify({
3799
+ client_id: OPENAI_CODEX_CLIENT_ID,
3800
+ scope: REQUIRED_SCOPES.join(" ")
3801
+ })
3802
+ });
3803
+ if (!response.ok) {
3804
+ const body = await response.text();
3805
+ throw new Error(
3806
+ `OpenAI Codex device authorization start failed (${response.status}): ${body.slice(0, 240)}`
3807
+ );
3808
+ }
3809
+ const data = await response.json();
3810
+ const intervalRaw = typeof data.interval === "number" ? data.interval : Number.parseInt(String(data.interval ?? "5"), 10);
3811
+ const intervalMs = Math.max(Number.isNaN(intervalRaw) ? 5 : intervalRaw, 1) * 1e3;
3812
+ return {
3813
+ deviceAuthId: data.device_auth_id,
3814
+ userCode: data.user_code,
3815
+ verificationUrl: `${OPENAI_AUTH_ISSUER}/codex/device`,
3816
+ intervalMs
3817
+ };
3818
+ };
3819
+ var completeOpenAICodexDeviceAuth = async (request) => {
3820
+ const startedAt = Date.now();
3821
+ while (Date.now() - startedAt < DEVICE_FLOW_TIMEOUT_MS) {
3822
+ const response = await fetch(`${OPENAI_AUTH_ISSUER}/api/accounts/deviceauth/token`, {
3823
+ method: "POST",
3824
+ headers: {
3825
+ "Content-Type": "application/json",
3826
+ "User-Agent": "poncho/1.0"
3827
+ },
3828
+ body: JSON.stringify({
3829
+ device_auth_id: request.deviceAuthId,
3830
+ user_code: request.userCode
3831
+ })
3832
+ });
3833
+ if (response.ok) {
3834
+ const data = await response.json();
3835
+ const tokens = await exchangeAuthorizationCode(
3836
+ data.authorization_code,
3837
+ data.code_verifier
3838
+ );
3839
+ if (!tokens.refresh_token || tokens.refresh_token.length === 0) {
3840
+ throw new Error("OpenAI Codex device auth succeeded but no refresh token was returned.");
3841
+ }
3842
+ return {
3843
+ refreshToken: tokens.refresh_token,
3844
+ accessToken: tokens.access_token,
3845
+ accessTokenExpiresAt: Date.now() + (tokens.expires_in ?? 3600) * 1e3,
3846
+ accountId: parseAccountIdFromJwt(tokens.id_token) ?? parseAccountIdFromJwt(tokens.access_token)
3847
+ };
3848
+ }
3849
+ if (response.status !== 403 && response.status !== 404) {
3850
+ const body = await response.text();
3851
+ throw new Error(
3852
+ `OpenAI Codex device authorization polling failed (${response.status}): ${body.slice(0, 240)}`
3853
+ );
3854
+ }
3855
+ await new Promise((resolveWait) => {
3856
+ setTimeout(resolveWait, request.intervalMs + DEVICE_POLLING_SAFETY_MARGIN_MS);
3857
+ });
3858
+ }
3859
+ throw new Error(
3860
+ "OpenAI Codex device authorization timed out. Re-run `poncho auth login --provider openai-codex --device`."
3861
+ );
3862
+ };
3863
+
3864
+ // src/model-factory.ts
3575
3865
  var MODEL_CONTEXT_WINDOWS = {
3576
3866
  "claude-opus-4-6": 2e5,
3577
3867
  "claude-sonnet-4-6": 2e5,
@@ -3596,6 +3886,29 @@ var MODEL_CONTEXT_WINDOWS = {
3596
3886
  "o3": 2e5
3597
3887
  };
3598
3888
  var DEFAULT_CONTEXT_WINDOW = 2e5;
3889
+ var OPENAI_CODEX_DEFAULT_INSTRUCTIONS = "You are Codex, based on GPT-5. You are running as a coding agent in Poncho.";
3890
+ var extractSystemInstructionFromInput = (input) => {
3891
+ if (!Array.isArray(input)) return void 0;
3892
+ for (const message of input) {
3893
+ if (!message || typeof message !== "object") continue;
3894
+ const candidate = message;
3895
+ if (candidate.role !== "system") continue;
3896
+ if (typeof candidate.content === "string" && candidate.content.trim().length > 0) {
3897
+ return candidate.content;
3898
+ }
3899
+ if (Array.isArray(candidate.content)) {
3900
+ const textParts = candidate.content.map((part) => {
3901
+ if (!part || typeof part !== "object") return "";
3902
+ const p = part;
3903
+ return typeof p.text === "string" ? p.text : "";
3904
+ }).filter((text) => text.trim().length > 0);
3905
+ if (textParts.length > 0) {
3906
+ return textParts.join("\n");
3907
+ }
3908
+ }
3909
+ }
3910
+ return void 0;
3911
+ };
3599
3912
  var getModelContextWindow = (modelName) => {
3600
3913
  if (MODEL_CONTEXT_WINDOWS[modelName] !== void 0) {
3601
3914
  return MODEL_CONTEXT_WINDOWS[modelName];
@@ -3610,6 +3923,39 @@ var getModelContextWindow = (modelName) => {
3610
3923
  };
3611
3924
  var createModelProvider = (provider, config) => {
3612
3925
  const normalized = (provider ?? "anthropic").toLowerCase();
3926
+ if (normalized === "openai-codex") {
3927
+ const openai = createOpenAI({
3928
+ apiKey: "oauth-placeholder",
3929
+ fetch: async (input, init) => {
3930
+ const { accessToken, accountId } = await getOpenAICodexAccessToken(config?.openaiCodex);
3931
+ const headers = new Headers(init?.headers);
3932
+ headers.set("Authorization", `Bearer ${accessToken}`);
3933
+ headers.set("originator", "poncho");
3934
+ headers.set("User-Agent", "poncho/1.0");
3935
+ if (accountId) {
3936
+ headers.set("ChatGPT-Account-Id", accountId);
3937
+ }
3938
+ const originalUrl = input instanceof URL ? input.toString() : typeof input === "string" ? input : input.url;
3939
+ const parsed = new URL(originalUrl);
3940
+ const shouldRewrite = parsed.pathname.includes("/v1/responses") || parsed.pathname.includes("/chat/completions");
3941
+ const targetUrl = shouldRewrite ? "https://chatgpt.com/backend-api/codex/responses" : originalUrl;
3942
+ let body = init?.body;
3943
+ if (shouldRewrite && typeof body === "string" && headers.get("Content-Type")?.includes("application/json")) {
3944
+ try {
3945
+ const payload = JSON.parse(body);
3946
+ if (typeof payload.instructions !== "string" || payload.instructions.trim() === "") {
3947
+ payload.instructions = extractSystemInstructionFromInput(payload.input) ?? OPENAI_CODEX_DEFAULT_INSTRUCTIONS;
3948
+ }
3949
+ payload.store = false;
3950
+ body = JSON.stringify(payload);
3951
+ } catch {
3952
+ }
3953
+ }
3954
+ return fetch(targetUrl, { ...init, headers, body });
3955
+ }
3956
+ });
3957
+ return (modelName) => openai(modelName);
3958
+ }
3613
3959
  if (normalized === "openai") {
3614
3960
  const apiKeyEnv2 = config?.openai?.apiKeyEnv ?? "OPENAI_API_KEY";
3615
3961
  const openai = createOpenAI({
@@ -3626,8 +3972,8 @@ var createModelProvider = (provider, config) => {
3626
3972
  };
3627
3973
 
3628
3974
  // src/skill-context.ts
3629
- import { readFile as readFile7, readdir as readdir2, stat } from "fs/promises";
3630
- import { dirname as dirname4, resolve as resolve8, normalize } from "path";
3975
+ import { readFile as readFile8, readdir as readdir2, stat } from "fs/promises";
3976
+ import { dirname as dirname5, resolve as resolve9, normalize } from "path";
3631
3977
  import YAML3 from "yaml";
3632
3978
  var DEFAULT_SKILL_DIRS = ["skills"];
3633
3979
  var resolveSkillDirs = (workingDir, extraPaths) => {
@@ -3639,7 +3985,7 @@ var resolveSkillDirs = (workingDir, extraPaths) => {
3639
3985
  }
3640
3986
  }
3641
3987
  }
3642
- return dirs.map((d) => resolve8(workingDir, d));
3988
+ return dirs.map((d) => resolve9(workingDir, d));
3643
3989
  };
3644
3990
  var FRONTMATTER_PATTERN3 = /^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/;
3645
3991
  var asRecord2 = (value) => typeof value === "object" && value !== null ? value : {};
@@ -3708,7 +4054,7 @@ var collectSkillManifests = async (directory) => {
3708
4054
  const entries = await readdir2(directory, { withFileTypes: true });
3709
4055
  const files = [];
3710
4056
  for (const entry of entries) {
3711
- const fullPath = resolve8(directory, entry.name);
4057
+ const fullPath = resolve9(directory, entry.name);
3712
4058
  let isDir = entry.isDirectory();
3713
4059
  let isFile = entry.isFile();
3714
4060
  if (entry.isSymbolicLink()) {
@@ -3743,13 +4089,13 @@ var loadSkillMetadata = async (workingDir, extraSkillPaths) => {
3743
4089
  const seen = /* @__PURE__ */ new Set();
3744
4090
  for (const manifest of allManifests) {
3745
4091
  try {
3746
- const content = await readFile7(manifest, "utf8");
4092
+ const content = await readFile8(manifest, "utf8");
3747
4093
  const parsed = parseSkillFrontmatter(content);
3748
4094
  if (parsed && !seen.has(parsed.name)) {
3749
4095
  seen.add(parsed.name);
3750
4096
  skills.push({
3751
4097
  ...parsed,
3752
- skillDir: dirname4(manifest),
4098
+ skillDir: dirname5(manifest),
3753
4099
  skillPath: manifest
3754
4100
  });
3755
4101
  }
@@ -3788,7 +4134,7 @@ ${xmlSkills}
3788
4134
  };
3789
4135
  var escapeXml = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
3790
4136
  var loadSkillInstructions = async (skill) => {
3791
- const content = await readFile7(skill.skillPath, "utf8");
4137
+ const content = await readFile8(skill.skillPath, "utf8");
3792
4138
  const match = content.match(FRONTMATTER_PATTERN3);
3793
4139
  return match ? match[2].trim() : content.trim();
3794
4140
  };
@@ -3797,11 +4143,11 @@ var readSkillResource = async (skill, relativePath) => {
3797
4143
  if (normalized.startsWith("..") || normalized.startsWith("/")) {
3798
4144
  throw new Error("Path must be relative and within the skill directory");
3799
4145
  }
3800
- const fullPath = resolve8(skill.skillDir, normalized);
4146
+ const fullPath = resolve9(skill.skillDir, normalized);
3801
4147
  if (!fullPath.startsWith(skill.skillDir)) {
3802
4148
  throw new Error("Path escapes the skill directory");
3803
4149
  }
3804
- return await readFile7(fullPath, "utf8");
4150
+ return await readFile8(fullPath, "utf8");
3805
4151
  };
3806
4152
  var MAX_INSTRUCTIONS_PER_SKILL = 1200;
3807
4153
  var loadSkillContext = async (workingDir) => {
@@ -3920,7 +4266,7 @@ function convertSchema(schema) {
3920
4266
  // src/skill-tools.ts
3921
4267
  import { defineTool as defineTool4 } from "@poncho-ai/sdk";
3922
4268
  import { access as access2, readdir as readdir3, stat as stat2 } from "fs/promises";
3923
- import { extname, normalize as normalize2, resolve as resolve9, sep as sep2 } from "path";
4269
+ import { extname, normalize as normalize2, resolve as resolve10, sep as sep2 } from "path";
3924
4270
  import { pathToFileURL } from "url";
3925
4271
  import { createJiti as createJiti2 } from "jiti";
3926
4272
  var createSkillTools = (skills, options) => {
@@ -4178,7 +4524,7 @@ var collectScriptFiles = async (directory) => {
4178
4524
  if (entry.name === "node_modules") {
4179
4525
  continue;
4180
4526
  }
4181
- const fullPath = resolve9(directory, entry.name);
4527
+ const fullPath = resolve10(directory, entry.name);
4182
4528
  let isDir = entry.isDirectory();
4183
4529
  let isFile = entry.isFile();
4184
4530
  if (entry.isSymbolicLink()) {
@@ -4217,8 +4563,8 @@ var normalizeScriptPolicyPath = (relativePath) => {
4217
4563
  };
4218
4564
  var resolveScriptPath = (baseDir, relativePath, containmentDir) => {
4219
4565
  const normalized = normalizeScriptPolicyPath(relativePath);
4220
- const fullPath = resolve9(baseDir, normalized);
4221
- const boundary = resolve9(containmentDir ?? baseDir);
4566
+ const fullPath = resolve10(baseDir, normalized);
4567
+ const boundary = resolve10(containmentDir ?? baseDir);
4222
4568
  if (!fullPath.startsWith(`${boundary}${sep2}`) && fullPath !== boundary) {
4223
4569
  throw new Error("Script path must stay inside the allowed directory");
4224
4570
  }
@@ -4324,8 +4670,8 @@ function applyRateLimitCooldown(retryAfterHeader) {
4324
4670
  async function runWithSearchThrottle(fn) {
4325
4671
  const previous = searchQueue;
4326
4672
  let release;
4327
- searchQueue = new Promise((resolve12) => {
4328
- release = resolve12;
4673
+ searchQueue = new Promise((resolve13) => {
4674
+ release = resolve13;
4329
4675
  });
4330
4676
  await previous.catch(() => {
4331
4677
  });
@@ -5122,6 +5468,8 @@ export default {
5122
5468
  providers: {
5123
5469
  anthropic: { apiKeyEnv: "ANTHROPIC_API_KEY" },
5124
5470
  openai: { apiKeyEnv: "OPENAI_API_KEY" },
5471
+ // openai-codex provider reads OAuth tokens from env vars by default:
5472
+ // openaiCodex: { refreshTokenEnv: "OPENAI_CODEX_REFRESH_TOKEN", accountIdEnv: "OPENAI_CODEX_ACCOUNT_ID" },
5125
5473
  },
5126
5474
  auth: {
5127
5475
  required: true,
@@ -5524,8 +5872,8 @@ var AgentHarness = class _AgentHarness {
5524
5872
  return false;
5525
5873
  }
5526
5874
  try {
5527
- const agentFilePath = resolve10(this.workingDir, "AGENT.md");
5528
- const rawContent = await readFile8(agentFilePath, "utf8");
5875
+ const agentFilePath = resolve11(this.workingDir, "AGENT.md");
5876
+ const rawContent = await readFile9(agentFilePath, "utf8");
5529
5877
  if (rawContent === this.agentFileFingerprint) {
5530
5878
  return false;
5531
5879
  }
@@ -5594,8 +5942,8 @@ var AgentHarness = class _AgentHarness {
5594
5942
  }
5595
5943
  }
5596
5944
  async initialize() {
5597
- const agentFilePath = resolve10(this.workingDir, "AGENT.md");
5598
- const agentRawContent = await readFile8(agentFilePath, "utf8");
5945
+ const agentFilePath = resolve11(this.workingDir, "AGENT.md");
5946
+ const agentRawContent = await readFile9(agentFilePath, "utf8");
5599
5947
  this.parsedAgent = parseAgentMarkdown(agentRawContent);
5600
5948
  this.agentFileFingerprint = agentRawContent;
5601
5949
  const identity = await ensureAgentIdentity(this.workingDir);
@@ -5702,14 +6050,14 @@ var AgentHarness = class _AgentHarness {
5702
6050
  const filePath = pathResolve(stateDir, `${sessionId}.json`);
5703
6051
  return {
5704
6052
  async save(json) {
5705
- const { mkdir: mkdir6, writeFile: writeFile7 } = await import("fs/promises");
5706
- await mkdir6(stateDir, { recursive: true });
5707
- await writeFile7(filePath, json, "utf8");
6053
+ const { mkdir: mkdir7, writeFile: writeFile8 } = await import("fs/promises");
6054
+ await mkdir7(stateDir, { recursive: true });
6055
+ await writeFile8(filePath, json, "utf8");
5708
6056
  },
5709
6057
  async load() {
5710
- const { readFile: readFile10 } = await import("fs/promises");
6058
+ const { readFile: readFile11 } = await import("fs/promises");
5711
6059
  try {
5712
- return await readFile10(filePath, "utf8");
6060
+ return await readFile11(filePath, "utf8");
5713
6061
  } catch {
5714
6062
  return void 0;
5715
6063
  }
@@ -5775,7 +6123,7 @@ var AgentHarness = class _AgentHarness {
5775
6123
  let browserMod;
5776
6124
  try {
5777
6125
  const { existsSync } = await import("fs");
5778
- const { join, dirname: dirname6 } = await import("path");
6126
+ const { join, dirname: dirname7 } = await import("path");
5779
6127
  const { pathToFileURL: pathToFileURL2 } = await import("url");
5780
6128
  let searchDir = this.workingDir;
5781
6129
  let entryPath;
@@ -5785,7 +6133,7 @@ var AgentHarness = class _AgentHarness {
5785
6133
  entryPath = candidate;
5786
6134
  break;
5787
6135
  }
5788
- const parent = dirname6(searchDir);
6136
+ const parent = dirname7(searchDir);
5789
6137
  if (parent === searchDir) break;
5790
6138
  searchDir = parent;
5791
6139
  }
@@ -5890,9 +6238,9 @@ var AgentHarness = class _AgentHarness {
5890
6238
  for await (const event of this.run(input)) {
5891
6239
  eventQueue.push(event);
5892
6240
  if (queueResolve) {
5893
- const resolve12 = queueResolve;
6241
+ const resolve13 = queueResolve;
5894
6242
  queueResolve = null;
5895
- resolve12();
6243
+ resolve13();
5896
6244
  }
5897
6245
  }
5898
6246
  } catch (error) {
@@ -5911,8 +6259,8 @@ var AgentHarness = class _AgentHarness {
5911
6259
  if (eventQueue.length > 0) {
5912
6260
  yield eventQueue.shift();
5913
6261
  } else if (!generatorDone) {
5914
- await new Promise((resolve12) => {
5915
- queueResolve = resolve12;
6262
+ await new Promise((resolve13) => {
6263
+ queueResolve = resolve13;
5916
6264
  });
5917
6265
  }
5918
6266
  }
@@ -6504,8 +6852,8 @@ ${textContent}` };
6504
6852
  let timer;
6505
6853
  nextPart = await Promise.race([
6506
6854
  fullStreamIterator.next(),
6507
- new Promise((resolve12) => {
6508
- timer = setTimeout(() => resolve12(null), effectiveTimeout);
6855
+ new Promise((resolve13) => {
6856
+ timer = setTimeout(() => resolve13(null), effectiveTimeout);
6509
6857
  })
6510
6858
  ]);
6511
6859
  clearTimeout(timer);
@@ -6822,7 +7170,7 @@ ${textContent}` };
6822
7170
  const raced = await Promise.race([
6823
7171
  this.dispatcher.executeBatch(approvedCalls, toolContext),
6824
7172
  new Promise(
6825
- (resolve12) => setTimeout(() => resolve12(TOOL_DEADLINE_SENTINEL), toolDeadlineRemainingMs)
7173
+ (resolve13) => setTimeout(() => resolve13(TOOL_DEADLINE_SENTINEL), toolDeadlineRemainingMs)
6826
7174
  )
6827
7175
  ]);
6828
7176
  if (raced === TOOL_DEADLINE_SENTINEL) {
@@ -7163,8 +7511,8 @@ var LatitudeCapture = class {
7163
7511
 
7164
7512
  // src/state.ts
7165
7513
  import { randomUUID as randomUUID4 } from "crypto";
7166
- import { mkdir as mkdir5, readFile as readFile9, readdir as readdir4, rename as rename3, rm as rm3, writeFile as writeFile6 } from "fs/promises";
7167
- import { dirname as dirname5, resolve as resolve11 } from "path";
7514
+ import { mkdir as mkdir6, readFile as readFile10, readdir as readdir4, rename as rename3, rm as rm4, writeFile as writeFile7 } from "fs/promises";
7515
+ import { dirname as dirname6, resolve as resolve12 } from "path";
7168
7516
  var DEFAULT_OWNER = "local-owner";
7169
7517
  var LOCAL_STATE_FILE = "state.json";
7170
7518
  var CONVERSATIONS_DIRECTORY = "conversations";
@@ -7180,9 +7528,9 @@ var toStoreIdentity = async ({
7180
7528
  return { name: ensured.name, id: agentId };
7181
7529
  };
7182
7530
  var writeJsonAtomic3 = async (filePath, payload) => {
7183
- await mkdir5(dirname5(filePath), { recursive: true });
7531
+ await mkdir6(dirname6(filePath), { recursive: true });
7184
7532
  const tmpPath = `${filePath}.tmp`;
7185
- await writeFile6(tmpPath, JSON.stringify(payload, null, 2), "utf8");
7533
+ await writeFile7(tmpPath, JSON.stringify(payload, null, 2), "utf8");
7186
7534
  await rename3(tmpPath, filePath);
7187
7535
  };
7188
7536
  var formatUtcTimestamp = (value) => new Date(value).toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
@@ -7371,8 +7719,8 @@ var FileConversationStore = class {
7371
7719
  agentId: this.agentId
7372
7720
  });
7373
7721
  const agentDir = getAgentStoreDirectory(identity);
7374
- const conversationsDir = resolve11(agentDir, CONVERSATIONS_DIRECTORY);
7375
- const indexPath = resolve11(conversationsDir, LOCAL_CONVERSATION_INDEX_FILE);
7722
+ const conversationsDir = resolve12(agentDir, CONVERSATIONS_DIRECTORY);
7723
+ const indexPath = resolve12(conversationsDir, LOCAL_CONVERSATION_INDEX_FILE);
7376
7724
  this.paths = { conversationsDir, indexPath };
7377
7725
  return this.paths;
7378
7726
  }
@@ -7386,9 +7734,9 @@ var FileConversationStore = class {
7386
7734
  }
7387
7735
  async readConversationFile(fileName) {
7388
7736
  const { conversationsDir } = await this.resolvePaths();
7389
- const filePath = resolve11(conversationsDir, fileName);
7737
+ const filePath = resolve12(conversationsDir, fileName);
7390
7738
  try {
7391
- const raw = await readFile9(filePath, "utf8");
7739
+ const raw = await readFile10(filePath, "utf8");
7392
7740
  return JSON.parse(raw);
7393
7741
  } catch {
7394
7742
  return void 0;
@@ -7434,7 +7782,7 @@ var FileConversationStore = class {
7434
7782
  this.loaded = true;
7435
7783
  const { indexPath } = await this.resolvePaths();
7436
7784
  try {
7437
- const raw = await readFile9(indexPath, "utf8");
7785
+ const raw = await readFile10(indexPath, "utf8");
7438
7786
  const parsed = JSON.parse(raw);
7439
7787
  for (const conversation of parsed.conversations ?? []) {
7440
7788
  this.conversations.set(conversation.conversationId, conversation);
@@ -7457,7 +7805,7 @@ var FileConversationStore = class {
7457
7805
  const { conversationsDir } = await this.resolvePaths();
7458
7806
  const existing = this.conversations.get(conversation.conversationId);
7459
7807
  const fileName = existing?.fileName ?? this.resolveConversationFileName(conversation);
7460
- const filePath = resolve11(conversationsDir, fileName);
7808
+ const filePath = resolve12(conversationsDir, fileName);
7461
7809
  this.writing = this.writing.then(async () => {
7462
7810
  await writeJsonAtomic3(filePath, conversation);
7463
7811
  this.conversations.set(conversation.conversationId, {
@@ -7555,7 +7903,7 @@ var FileConversationStore = class {
7555
7903
  if (removed) {
7556
7904
  this.writing = this.writing.then(async () => {
7557
7905
  if (existing) {
7558
- await rm3(resolve11(conversationsDir, existing.fileName), { force: true });
7906
+ await rm4(resolve12(conversationsDir, existing.fileName), { force: true });
7559
7907
  }
7560
7908
  await this.writeIndex();
7561
7909
  });
@@ -7577,7 +7925,7 @@ var FileConversationStore = class {
7577
7925
  const summary = this.conversations.get(conversationId);
7578
7926
  if (!summary) return void 0;
7579
7927
  const { conversationsDir } = await this.resolvePaths();
7580
- const filePath = resolve11(conversationsDir, summary.fileName);
7928
+ const filePath = resolve12(conversationsDir, summary.fileName);
7581
7929
  let result;
7582
7930
  this.writing = this.writing.then(async () => {
7583
7931
  const conv = await this.readConversationFile(summary.fileName);
@@ -7616,7 +7964,7 @@ var FileStateStore = class {
7616
7964
  workingDir: this.workingDir,
7617
7965
  agentId: this.agentId
7618
7966
  });
7619
- this.filePath = resolve11(getAgentStoreDirectory(identity), LOCAL_STATE_FILE);
7967
+ this.filePath = resolve12(getAgentStoreDirectory(identity), LOCAL_STATE_FILE);
7620
7968
  }
7621
7969
  isExpired(state) {
7622
7970
  return typeof this.ttlMs === "number" && Date.now() - state.updatedAt > this.ttlMs;
@@ -7628,7 +7976,7 @@ var FileStateStore = class {
7628
7976
  }
7629
7977
  this.loaded = true;
7630
7978
  try {
7631
- const raw = await readFile9(this.filePath, "utf8");
7979
+ const raw = await readFile10(this.filePath, "utf8");
7632
7980
  const parsed = JSON.parse(raw);
7633
7981
  for (const state of parsed.states ?? []) {
7634
7982
  this.states.set(state.runId, state);
@@ -8337,6 +8685,7 @@ export {
8337
8685
  LatitudeCapture,
8338
8686
  LocalMcpBridge,
8339
8687
  LocalUploadStore,
8688
+ OPENAI_CODEX_CLIENT_ID,
8340
8689
  PONCHO_UPLOAD_SCHEME,
8341
8690
  S3UploadStore,
8342
8691
  STORAGE_SCHEMA_VERSION,
@@ -8346,6 +8695,7 @@ export {
8346
8695
  buildAgentDirectoryName,
8347
8696
  buildSkillContextWindow,
8348
8697
  compactMessages,
8698
+ completeOpenAICodexDeviceAuth,
8349
8699
  createConversationStore,
8350
8700
  createDefaultTools,
8351
8701
  createDeleteDirectoryTool,
@@ -8361,6 +8711,7 @@ export {
8361
8711
  createUploadStore,
8362
8712
  createWriteTool,
8363
8713
  defineTool7 as defineTool,
8714
+ deleteOpenAICodexSession,
8364
8715
  deriveUploadKey,
8365
8716
  ensureAgentIdentity,
8366
8717
  estimateTokens,
@@ -8369,6 +8720,9 @@ export {
8369
8720
  generateAgentId,
8370
8721
  getAgentStoreDirectory,
8371
8722
  getModelContextWindow,
8723
+ getOpenAICodexAccessToken,
8724
+ getOpenAICodexAuthFilePath,
8725
+ getOpenAICodexRequiredScopes,
8372
8726
  getPonchoStoreRoot,
8373
8727
  jsonSchemaToZod,
8374
8728
  loadPonchoConfig,
@@ -8380,6 +8734,7 @@ export {
8380
8734
  parseAgentFile,
8381
8735
  parseAgentMarkdown,
8382
8736
  ponchoDocsTool,
8737
+ readOpenAICodexSession,
8383
8738
  readSkillResource,
8384
8739
  renderAgentPrompt,
8385
8740
  resolveAgentIdentity,
@@ -8387,5 +8742,7 @@ export {
8387
8742
  resolveMemoryConfig,
8388
8743
  resolveSkillDirs,
8389
8744
  resolveStateConfig,
8390
- slugifyStorageComponent
8745
+ slugifyStorageComponent,
8746
+ startOpenAICodexDeviceAuth,
8747
+ writeOpenAICodexSession
8391
8748
  };