@skydiveai/pi-extensions 0.1.0-beta.6 → 0.1.0-beta.61

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.
Files changed (2) hide show
  1. package/dist/index.mjs +44 -9
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -1949,11 +1949,33 @@ const subscribers = {
1949
1949
  subagent: /* @__PURE__ */ new Set()
1950
1950
  };
1951
1951
  let pollerStarted = false;
1952
+ let firstPollSettled = false;
1953
+ let resolveFirstPoll = null;
1954
+ const firstPollPromise = new Promise((resolve) => {
1955
+ resolveFirstPoll = resolve;
1956
+ });
1957
+ function markFirstPollSettled() {
1958
+ if (firstPollSettled) return;
1959
+ firstPollSettled = true;
1960
+ resolveFirstPoll?.();
1961
+ }
1952
1962
  /** Last-polled value of a flag, or `null` if not yet resolved. */
1953
1963
  function getPolledFlag(name) {
1954
1964
  return name === "contextManagement" ? contextManagement : subagent;
1955
1965
  }
1956
1966
  /**
1967
+ * Await the first poll already kicked by `startFeatureFlagPoller` (never a new
1968
+ * GET). Resolves when that poll settles, immediately if it already has, or
1969
+ * immediately when there's no flag source to poll. Callers on the hot path
1970
+ * should race this against their own short timeout so a slow/failed flag
1971
+ * service cannot delay first-token; a timeout just means the caller reads the
1972
+ * still-cold cache and falls back to its default, exactly as before.
1973
+ */
1974
+ function awaitFirstFlagPoll() {
1975
+ if (firstPollSettled || !hasFlagSource()) return Promise.resolve();
1976
+ return firstPollPromise;
1977
+ }
1978
+ /**
1957
1979
  * Subscribe to changes of a flag. The callback fires only on a *transition*
1958
1980
  * (skipped while the value is unchanged), so a subscriber registered before the
1959
1981
  * first poll still learns the initial value. Returns an unsubscribe fn.
@@ -1977,10 +1999,14 @@ function apply(name, next) {
1977
1999
  }
1978
2000
  }
1979
2001
  async function pollOnce() {
1980
- const flags = await fetchHarnessFlags();
1981
- if (!flags) return;
1982
- apply("contextManagement", flags.contextManagement ?? null);
1983
- apply("subagent", flags.subagent ?? null);
2002
+ try {
2003
+ const flags = await fetchHarnessFlags();
2004
+ if (!flags) return;
2005
+ apply("contextManagement", flags.contextManagement ?? null);
2006
+ apply("subagent", flags.subagent ?? null);
2007
+ } catch (err) {
2008
+ log$9.debug({ err }, "feature-flag poll threw");
2009
+ }
1984
2010
  }
1985
2011
  /**
1986
2012
  * Start the shared background poll (idempotent). No-op when there's no
@@ -1991,7 +2017,7 @@ async function pollOnce() {
1991
2017
  function startFeatureFlagPoller() {
1992
2018
  if (pollerStarted || !hasFlagSource()) return;
1993
2019
  pollerStarted = true;
1994
- pollOnce();
2020
+ pollOnce().finally(markFirstPollSettled);
1995
2021
  setInterval(() => void pollOnce(), FLAG_POLL_INTERVAL_MS).unref?.();
1996
2022
  }
1997
2023
  //#endregion
@@ -2755,6 +2781,7 @@ const soulExtension = (pi) => {
2755
2781
  //#endregion
2756
2782
  //#region src/extensions/subagent/index.ts
2757
2783
  const log$2 = logger.child({ module: "subagent-ext" });
2784
+ const COLD_START_FLAG_WAIT_MS = 750;
2758
2785
  const MAX_TASKS = 8;
2759
2786
  const TaskItem = Type.Object({
2760
2787
  task: Type.String({ description: "The task to delegate to a subagent run." }),
@@ -2840,11 +2867,19 @@ function createSubagentExtension({ channelContext }) {
2840
2867
  return (pi) => {
2841
2868
  const messageId = extractMessageId(channelContext);
2842
2869
  startFeatureFlagPoller();
2870
+ let registered = false;
2871
+ const registerOnce = () => {
2872
+ if (registered) return;
2873
+ registered = true;
2874
+ pi.registerTool(buildTool(messageId));
2875
+ log$2.info({ event: "subagent_enabled" }, "subagent tool registered");
2876
+ };
2877
+ onFlagChange("subagent", (enabled) => {
2878
+ if (enabled) registerOnce();
2879
+ });
2843
2880
  pi.on("session_start", async () => {
2844
- if (getPolledFlag("subagent") === true) {
2845
- pi.registerTool(buildTool(messageId));
2846
- log$2.info({ event: "subagent_enabled" }, "subagent tool registered");
2847
- }
2881
+ if (getPolledFlag("subagent") === null) await Promise.race([awaitFirstFlagPoll(), new Promise((resolve) => setTimeout(resolve, COLD_START_FLAG_WAIT_MS).unref?.())]);
2882
+ if (getPolledFlag("subagent") === true) registerOnce();
2848
2883
  });
2849
2884
  };
2850
2885
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skydiveai/pi-extensions",
3
- "version": "0.1.0-beta.6",
3
+ "version": "0.1.0-beta.61",
4
4
  "homepage": "https://skydive.com",
5
5
  "license": "MIT",
6
6
  "author": "Create, Inc.",