@skydiveai/pi-extensions 0.1.0-beta.863 → 0.1.0-beta.867

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 +59 -1
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -1683,6 +1683,23 @@ function apiBaseUrl() {
1683
1683
  }
1684
1684
  //#endregion
1685
1685
  //#region src/extensions/platform.ts
1686
+ /**
1687
+ * Platform extension — bridges the agent harness to the Skydive platform daemon.
1688
+ *
1689
+ * Responsibilities:
1690
+ * - Heartbeat: periodic POST to the API so the sandbox manager knows the
1691
+ * agent is alive. Throttled to once per minute, triggered by tool events.
1692
+ * - Session tracking: registers the session with the daemon on start,
1693
+ * streams tool_call / tool_result events so the daemon can track which
1694
+ * session is actively executing, and signals session end on agent_end.
1695
+ * - Channel context: passes the SKYDIVE_CHANNEL_CONTEXT (containing the
1696
+ * messageId) to the daemon so file writes can be attributed to the
1697
+ * correct conversation.
1698
+ *
1699
+ * All daemon POSTs are fire-and-forget — failures are logged but never
1700
+ * block the agent. The daemon may not be running (e.g. local dev without
1701
+ * a sandbox), and that's fine.
1702
+ */
1686
1703
  const HEARTBEAT_THROTTLE_MS = 6e4;
1687
1704
  const TOOL_HEARTBEAT_INTERVAL_MS = 5e3;
1688
1705
  const MAX_TOOL_HEARTBEATS = 1440 * 60 * 1e3 / TOOL_HEARTBEAT_INTERVAL_MS;
@@ -1694,9 +1711,49 @@ function sandboxClient() {
1694
1711
  return hc(`${apiUrl}/api/v1/sandbox`);
1695
1712
  }
1696
1713
  /**
1714
+ * Is this box still an unclaimed warm-pool sandbox? (ANY-6000, the
1715
+ * feature-flags half of the ANY-5184 pool 403 wave.)
1716
+ *
1717
+ * `GET /sandbox/feature-flags` is agent-only, so the shared poller's request
1718
+ * from a pool box can only 403 — a guaranteed-failing GET every 60s for the
1719
+ * life of the pool phase. The discriminator is the sandbox token's `type`
1720
+ * claim, read UNVERIFIED (this box never holds the signing secret): not an
1721
+ * authorization decision, only "should I bother calling?", and the api still
1722
+ * authorizes every request.
1723
+ *
1724
+ * Read per call from the daemon's persisted env file, NOT process.env:
1725
+ * claiming a pool box rebinds the token in place (the daemon rewrites this
1726
+ * file) while the harness's process.env keeps the boot snapshot, so a
1727
+ * process-env gate would leave a claimed box permanently skipping — trading a
1728
+ * wasted request for silently frozen flags, which is strictly worse. "Cannot
1729
+ * tell" (no file, no token, unparseable payload) reports false so the poll
1730
+ * proceeds.
1731
+ */
1732
+ const daemonEnvIdentitySchema = z.object({
1733
+ ANYONE_SANDBOX_TOKEN: z.string().optional(),
1734
+ SKYDIVE_SANDBOX_TOKEN: z.string().optional()
1735
+ }).passthrough();
1736
+ const tokenTypeSchema = z.object({ type: z.string() }).passthrough();
1737
+ async function isPoolIdentity() {
1738
+ try {
1739
+ const envFile = process.env.ANYONE_DAEMON_ENV_CACHE ?? "/tmp/.anyone/daemon-env.json";
1740
+ const env = daemonEnvIdentitySchema.safeParse(JSON.parse(await readFile(envFile, "utf8")));
1741
+ if (!env.success) return false;
1742
+ const token = env.data.ANYONE_SANDBOX_TOKEN ?? env.data.SKYDIVE_SANDBOX_TOKEN;
1743
+ if (typeof token !== "string" || token === "") return false;
1744
+ const payload = token.split(".")[1];
1745
+ if (!payload) return false;
1746
+ const claims = tokenTypeSchema.safeParse(JSON.parse(Buffer.from(payload, "base64url").toString("utf8")));
1747
+ return claims.success && claims.data.type === "onboarding-pool";
1748
+ } catch (_err) {
1749
+ return false;
1750
+ }
1751
+ }
1752
+ /**
1697
1753
  * Fetch every harness feature flag in one GET (`{ contextManagement, ... }`
1698
1754
  * — see apps/anyone/api/src/routes/sandbox-feature-flags.ts). Returns
1699
- * null when indeterminate (no api url, or the request failed) so the shared
1755
+ * null when indeterminate (no api url, the request failed, or the box is an
1756
+ * unclaimed pool sandbox whose token the route would 403) so the shared
1700
1757
  * poller keeps the last-known values rather than flipping on a transient error.
1701
1758
  * This is the single fetch behind `feature-flags-poll.ts`; extensions read the
1702
1759
  * polled values there instead of issuing their own GET.
@@ -1704,6 +1761,7 @@ function sandboxClient() {
1704
1761
  async function fetchHarnessFlags() {
1705
1762
  const client = sandboxClient();
1706
1763
  if (!client) return null;
1764
+ if (await isPoolIdentity()) return null;
1707
1765
  try {
1708
1766
  const res = await client["feature-flags"].$get();
1709
1767
  if (!res.ok) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skydiveai/pi-extensions",
3
- "version": "0.1.0-beta.863",
3
+ "version": "0.1.0-beta.867",
4
4
  "homepage": "https://skydive.com",
5
5
  "license": "MIT",
6
6
  "author": "Create, Inc.",