@skydiveai/pi-extensions 0.1.0-beta.245 → 0.1.0-beta.252

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 +18 -50
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -1691,8 +1691,8 @@ function sandboxClient() {
1691
1691
  return hc(`${apiUrl}/api/v1/sandbox`);
1692
1692
  }
1693
1693
  /**
1694
- * Fetch every harness feature flag in one GET (`{ contextManagement, subagent,
1695
- * ... }` — see apps/anyone/api/src/routes/sandbox-feature-flags.ts). Returns
1694
+ * Fetch every harness feature flag in one GET (`{ contextManagement, ... }`
1695
+ * — see apps/anyone/api/src/routes/sandbox-feature-flags.ts). Returns
1696
1696
  * null when indeterminate (no api url, or the request failed) so the shared
1697
1697
  * poller keeps the last-known values rather than flipping on a transient error.
1698
1698
  * This is the single fetch behind `feature-flags-poll.ts`; extensions read the
@@ -1710,11 +1710,7 @@ async function fetchHarnessFlags() {
1710
1710
  }, "feature-flags fetch failed");
1711
1711
  return null;
1712
1712
  }
1713
- const body = await res.json();
1714
- return {
1715
- contextManagement: body.contextManagement ?? null,
1716
- subagent: body.subagent ?? null
1717
- };
1713
+ return { contextManagement: (await res.json()).contextManagement ?? null };
1718
1714
  } catch (err) {
1719
1715
  log$10.debug({
1720
1716
  err,
@@ -1922,20 +1918,16 @@ function createPlatformExtensions({ sessionId, channelContext }) {
1922
1918
  * Shared harness feature-flag poll.
1923
1919
  *
1924
1920
  * The api exposes one `/feature-flags` GET that returns every harness flag in a
1925
- * single response (`{ contextManagement, subagent, commandFlags }` — see
1921
+ * single response (`{ contextManagement, commandFlags }` — see
1926
1922
  * apps/anyone/api/src/routes/sandbox-feature-flags.ts). Rather than each
1927
1923
  * extension issuing its own GET — and, worse, a *blocking* GET on the
1928
1924
  * pre-first-token `session_start` path — a single background poller fetches
1929
1925
  * that response once per interval and fans the values out to every subscriber.
1930
1926
  *
1931
- * Why one poller: the subagent extension gates its tool registration on the
1932
- * `subagent` flag. If it awaited a fresh GET inside `session_start` the tool
1933
- * schema (part of the prefill) couldn't be finalized until a serial
1934
- * sandbox→api round-trip settled, adding a net-new pre-token network hop on
1935
- * every session, flag on or off. Reading the last-polled value instead keeps
1936
- * the hot path allocation-only. A cold cache reads as `null` (fail-open to
1937
- * unregistered); a newly-flipped flag takes effect on the next poll, matching
1938
- * how context-management already treats its flag.
1927
+ * Why one poller: context-management consumes the `contextManagement` flag
1928
+ * without a blocking GET on the pre-first-token `session_start` path. Reading
1929
+ * the last-polled value keeps the hot path allocation-only; a cold cache reads
1930
+ * as `null` and a newly-flipped flag takes effect on the next poll.
1939
1931
  *
1940
1932
  * The poll is fire-and-forget and self-unref'd — it never keeps the process
1941
1933
  * alive and an indeterminate result (no api url / transient failure) leaves the
@@ -1944,15 +1936,11 @@ function createPlatformExtensions({ sessionId, channelContext }) {
1944
1936
  const log$9 = logger.child({ module: "feature-flags-poll" });
1945
1937
  const FLAG_POLL_INTERVAL_MS = 6e4;
1946
1938
  let contextManagement = null;
1947
- let subagent = null;
1948
- const subscribers = {
1949
- contextManagement: /* @__PURE__ */ new Set(),
1950
- subagent: /* @__PURE__ */ new Set()
1951
- };
1939
+ const subscribers = { contextManagement: /* @__PURE__ */ new Set() };
1952
1940
  let pollerStarted = false;
1953
1941
  let firstPollSettled = false;
1954
1942
  let resolveFirstPoll = null;
1955
- const firstPollPromise = new Promise((resolve) => {
1943
+ new Promise((resolve) => {
1956
1944
  resolveFirstPoll = resolve;
1957
1945
  });
1958
1946
  function markFirstPollSettled() {
@@ -1961,20 +1949,8 @@ function markFirstPollSettled() {
1961
1949
  resolveFirstPoll?.();
1962
1950
  }
1963
1951
  /** Last-polled value of a flag, or `null` if not yet resolved. */
1964
- function getPolledFlag(name) {
1965
- return name === "contextManagement" ? contextManagement : subagent;
1966
- }
1967
- /**
1968
- * Await the first poll already kicked by `startFeatureFlagPoller` (never a new
1969
- * GET). Resolves when that poll settles, immediately if it already has, or
1970
- * immediately when there's no flag source to poll. Callers on the hot path
1971
- * should race this against their own short timeout so a slow/failed flag
1972
- * service cannot delay first-token; a timeout just means the caller reads the
1973
- * still-cold cache and falls back to its default, exactly as before.
1974
- */
1975
- function awaitFirstFlagPoll() {
1976
- if (firstPollSettled || !hasFlagSource()) return Promise.resolve();
1977
- return firstPollPromise;
1952
+ function getPolledFlag(_name) {
1953
+ return contextManagement;
1978
1954
  }
1979
1955
  /**
1980
1956
  * Subscribe to changes of a flag. The callback fires only on a *transition*
@@ -1987,9 +1963,8 @@ function onFlagChange(name, cb) {
1987
1963
  }
1988
1964
  function apply(name, next) {
1989
1965
  if (next === null) return;
1990
- const prev = name === "contextManagement" ? contextManagement : subagent;
1991
- if (name === "contextManagement") contextManagement = next;
1992
- else subagent = next;
1966
+ const prev = contextManagement;
1967
+ contextManagement = next;
1993
1968
  if (next !== prev) for (const cb of subscribers[name]) try {
1994
1969
  cb(next);
1995
1970
  } catch (err) {
@@ -2004,7 +1979,6 @@ async function pollOnce() {
2004
1979
  const flags = await fetchHarnessFlags();
2005
1980
  if (!flags) return;
2006
1981
  apply("contextManagement", flags.contextManagement ?? null);
2007
- apply("subagent", flags.subagent ?? null);
2008
1982
  } catch (err) {
2009
1983
  log$9.debug({ err }, "feature-flag poll threw");
2010
1984
  }
@@ -2782,7 +2756,6 @@ const soulExtension = (pi) => {
2782
2756
  //#endregion
2783
2757
  //#region src/extensions/subagent/index.ts
2784
2758
  const log$2 = logger.child({ module: "subagent-ext" });
2785
- const COLD_START_FLAG_WAIT_MS = 750;
2786
2759
  const MAX_TASKS = 8;
2787
2760
  const TaskItem = Type.Object({
2788
2761
  task: Type.String({ description: "The task to delegate to a subagent run." }),
@@ -2863,16 +2836,15 @@ function buildTool(messageId) {
2863
2836
  };
2864
2837
  }
2865
2838
  /**
2866
- * Gated on `harness-subagent-enabled`, read from the shared feature-flag poll.
2867
2839
  * The factory takes the session's channel context to resolve the originating
2868
2840
  * messageId — the api links each spawned run to the conversation that message
2869
2841
  * belongs to and rewakes it on completion (nothing about the parent is piped
2870
- * from the sandbox beyond that id).
2842
+ * from the sandbox beyond that id). The tool is registered unconditionally at
2843
+ * session_start.
2871
2844
  */
2872
2845
  function createSubagentExtension({ channelContext }) {
2873
2846
  return (pi) => {
2874
2847
  const messageId = extractMessageId(channelContext);
2875
- startFeatureFlagPoller();
2876
2848
  let registered = false;
2877
2849
  const registerOnce = () => {
2878
2850
  if (registered) return;
@@ -2880,12 +2852,8 @@ function createSubagentExtension({ channelContext }) {
2880
2852
  pi.registerTool(buildTool(messageId));
2881
2853
  log$2.info({ event: "subagent_enabled" }, "subagent tool registered");
2882
2854
  };
2883
- onFlagChange("subagent", (enabled) => {
2884
- if (enabled) registerOnce();
2885
- });
2886
- pi.on("session_start", async () => {
2887
- if (getPolledFlag("subagent") === null) await Promise.race([awaitFirstFlagPoll(), new Promise((resolve) => setTimeout(resolve, COLD_START_FLAG_WAIT_MS).unref?.())]);
2888
- if (getPolledFlag("subagent") === true) registerOnce();
2855
+ pi.on("session_start", () => {
2856
+ registerOnce();
2889
2857
  });
2890
2858
  };
2891
2859
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skydiveai/pi-extensions",
3
- "version": "0.1.0-beta.245",
3
+ "version": "0.1.0-beta.252",
4
4
  "homepage": "https://skydive.com",
5
5
  "license": "MIT",
6
6
  "author": "Create, Inc.",