@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.
- package/dist/index.mjs +18 -50
- 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,
|
|
1695
|
-
*
|
|
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
|
-
|
|
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,
|
|
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:
|
|
1932
|
-
*
|
|
1933
|
-
*
|
|
1934
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
1965
|
-
return
|
|
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 =
|
|
1991
|
-
|
|
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
|
-
|
|
2884
|
-
|
|
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
|
}
|