@agentproto/runtime 3.2.0 → 3.3.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.d.ts CHANGED
@@ -3899,8 +3899,12 @@ type WorktreeGcClass = "reclaim" | "salvage" | "hold";
3899
3899
  * `orphan` is set on an entry/outcome the orphan scan found — a directory
3900
3900
  * physically present under the repo's worktree pool with no `git worktree
3901
3901
  * list` entry at all (see `WorktreeGcPlanEntryView.orphan`).
3902
+ * `prunable` is set on an entry/outcome `git worktree list --porcelain`
3903
+ * itself already reported dead — the mirror image of `orphan`: the
3904
+ * registration is still there, but the working directory is gone (see
3905
+ * `WorktreeGcPlanEntryView.prunable`).
3902
3906
  */
3903
- type WorktreeGcReclaimReason = "dep-bump" | "orphan";
3907
+ type WorktreeGcReclaimReason = "dep-bump" | "orphan" | "prunable";
3904
3908
  /**
3905
3909
  * One entry of the dry-run plan — a runtime-local projection of a
3906
3910
  * `GcPlanEntry`. `tree` / `integration` / `liveness` are flattened to their
@@ -3912,7 +3916,7 @@ interface WorktreeGcPlanEntryView {
3912
3916
  branch: string | null;
3913
3917
  head: string;
3914
3918
  class: WorktreeGcClass;
3915
- /** Set only when `class === "reclaim"` via the dep-bump exemption or the orphan scan. */
3919
+ /** Set only when `class === "reclaim"` via the dep-bump exemption, the orphan scan, or the prunable signal. */
3916
3920
  reclaimReason?: WorktreeGcReclaimReason;
3917
3921
  /**
3918
3922
  * `true` only for an orphan-scan entry: a directory physically present
@@ -3923,6 +3927,13 @@ interface WorktreeGcPlanEntryView {
3923
3927
  * literal `"orphan"` placeholder below rather than a fabricated value.
3924
3928
  */
3925
3929
  orphan?: boolean;
3930
+ /**
3931
+ * `true` only for a linked worktree `git worktree list --porcelain` itself
3932
+ * already marked `prunable`. Same reasoning as `orphan` above — no
3933
+ * tree/integration/liveness axis was ever read for a working directory
3934
+ * that's gone — projected as the literal `"prunable"` placeholder below.
3935
+ */
3936
+ prunable?: boolean;
3926
3937
  tree: string;
3927
3938
  integration: {
3928
3939
  state: string;
package/dist/index.mjs CHANGED
@@ -25,7 +25,7 @@ import { inferLegacyModeKind, parseModelSwitchCommand, isModelSwitchAcknowledgem
25
25
  import { loadSandboxConfig, resolveCommandSandbox, COMMAND_SANDBOX_MODE_ENV } from '@agentproto/command-sandbox';
26
26
  import { createBrainManager, parseKnowledgeConfig } from '@agentproto/workspace-brain';
27
27
  import { defineHttpDriver } from '@agentproto/driver-http';
28
- import { makeSessionsPanelApp, makeAgentsOverviewApp, makeBureauSessionsApp, makeSessionStoryPanelApp, makeLiveSessionApp, makeSessionChatApp, makeWorkBoardApp, SESSION_CHAT_APP_ID, sessionsPanelApp, agentsOverviewApp, bureauSessionsApp, sessionStoryApp, liveSessionApp, sessionChatApp, workBoardApp } from '@agentproto/apps';
28
+ import { liveSessionApp, makeLiveSessionApp, sessionsPanelApp, agentsOverviewApp, bureauSessionsApp, sessionStoryApp, workBoardApp, makeSessionsPanelApp, makeAgentsOverviewApp, makeBureauSessionsApp, makeSessionStoryPanelApp, makeSessionChatApp, makeWorkBoardApp, SESSION_CHAT_APP_ID, sessionChatApp } from '@agentproto/apps';
29
29
  import matter2 from 'gray-matter';
30
30
  import { createServer } from 'http';
31
31
  import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
@@ -20343,6 +20343,18 @@ var PANEL_APP_HANDLES = [
20343
20343
  sessionChatApp,
20344
20344
  workBoardApp
20345
20345
  ];
20346
+ function resolveBuiltinPanelUi(appId, httpBaseUrl) {
20347
+ if (appId === liveSessionApp.id) {
20348
+ const app = makeLiveSessionApp({ httpBaseUrl });
20349
+ const html = typeof app.html === "function" ? app.html({ httpBaseUrl }) : app.html;
20350
+ return { html, tools: liveSessionApp.ui?.tools ?? [] };
20351
+ }
20352
+ const handle = [sessionsPanelApp, agentsOverviewApp, bureauSessionsApp, sessionStoryApp, workBoardApp].find(
20353
+ (h) => h.id === appId
20354
+ );
20355
+ if (!handle?.ui) return void 0;
20356
+ return { html: handle.ui.html, tools: handle.ui.tools ?? [] };
20357
+ }
20346
20358
  function builtinPanelCatalogEntries() {
20347
20359
  const apps = makeBuiltinPanelApps({
20348
20360
  listSessions: () => [],
@@ -21953,12 +21965,7 @@ function notEnabled(tool) {
21953
21965
  `${tool} is not enabled \u2014 the daemon was started without an adapter resolver. Re-run the daemon with the \`@agentproto/cli\` shim wired (see playground/scripts/gateway.ts).`
21954
21966
  );
21955
21967
  }
21956
- async function performAppToolCall(appRegistry, input, deps2) {
21957
- const installed = appRegistry.getApp(input.appId);
21958
- if (!installed || !installed.ui) {
21959
- return errorResult2(`app_tool_call: app "${input.appId}" is not installed or has no UI.`);
21960
- }
21961
- const declaredAllowlist = installed.ui.tools ?? [];
21968
+ async function dispatchAllowlistedAppTool(declaredAllowlist, input, deps2) {
21962
21969
  const effectiveAllowlist = [...declaredAllowlist, ...APP_UI_DISCOVERY_TOOLS];
21963
21970
  if (!effectiveAllowlist.includes(input.tool)) {
21964
21971
  return errorResult2(
@@ -21986,6 +21993,19 @@ async function performAppToolCall(appRegistry, input, deps2) {
21986
21993
  return errorResult2(`app_tool_call: ${err instanceof Error ? err.message : String(err)}`);
21987
21994
  }
21988
21995
  }
21996
+ async function performAppToolCall(appRegistry, input, deps2) {
21997
+ const installed = appRegistry.getApp(input.appId);
21998
+ if (!installed || !installed.ui) {
21999
+ return errorResult2(`app_tool_call: app "${input.appId}" is not installed or has no UI.`);
22000
+ }
22001
+ return dispatchAllowlistedAppTool(installed.ui.tools ?? [], input, deps2);
22002
+ }
22003
+ async function performBuiltinPanelToolCall(tools, input, deps2) {
22004
+ if (tools === void 0) {
22005
+ return errorResult2(`app_tool_call: app "${input.appId}" is not installed or has no UI.`);
22006
+ }
22007
+ return dispatchAllowlistedAppTool(tools, input, deps2);
22008
+ }
21989
22009
  function refIdOf(ref) {
21990
22010
  if (typeof ref === "string") return ref;
21991
22011
  return ref.ref ?? ref.file ?? "inline";
@@ -26688,6 +26708,7 @@ var DEFAULT_ALLOWED_ORIGINS = [
26688
26708
  // matching how guilde.work is trusted. Drop it via `strictOrigins`.
26689
26709
  "https://cli.agentproto.sh"
26690
26710
  ];
26711
+ var DEFAULT_FRAME_ANCESTORS = ["vscode-webview:"];
26691
26712
  var PROXY_FORWARDING_HEADERS = [
26692
26713
  "x-forwarded-for",
26693
26714
  "forwarded",
@@ -28050,7 +28071,8 @@ async function startHttpServer(opts) {
28050
28071
  req,
28051
28072
  res,
28052
28073
  decodeURIComponent(uiMatch[1]),
28053
- opts.appRegistry
28074
+ opts.appRegistry,
28075
+ [...DEFAULT_FRAME_ANCESTORS, ...opts.frameAncestors ?? []]
28054
28076
  );
28055
28077
  return;
28056
28078
  }
@@ -28601,7 +28623,9 @@ function parseMcpServersField(raw) {
28601
28623
  servers.push({
28602
28624
  name: o.name,
28603
28625
  transport: o.transport,
28604
- ...typeof o.ref === "string" ? { ref: o.ref } : {}
28626
+ ...typeof o.ref === "string" ? { ref: o.ref } : {},
28627
+ ...isStringRecord(o.headers) ? { headers: o.headers } : {},
28628
+ ...typeof o.credentialRef === "string" ? { credentialRef: o.credentialRef } : {}
28605
28629
  });
28606
28630
  }
28607
28631
  return servers;
@@ -30749,33 +30773,43 @@ async function handleProviderInbound(req, res, slug, deps2) {
30749
30773
  res.writeHead(200, { "content-type": "application/json" });
30750
30774
  res.end(JSON.stringify(result));
30751
30775
  }
30752
- async function handleAppUiPage(req, res, appId, appRegistry) {
30776
+ function requestHttpBaseUrl(req) {
30777
+ return `http://${req.headers.host ?? "127.0.0.1"}`;
30778
+ }
30779
+ async function handleAppUiPage(req, res, appId, appRegistry, frameAncestors) {
30753
30780
  const app = appRegistry.getApp(appId);
30754
- if (!app?.ui) {
30781
+ const builtin = app?.ui ? void 0 : resolveBuiltinPanelUi(appId, requestHttpBaseUrl(req));
30782
+ if (!app?.ui && !builtin) {
30755
30783
  res.writeHead(404, { "content-type": "application/json" });
30756
30784
  res.end(JSON.stringify({ error: `app "${appId}" is not installed or has no UI.` }));
30757
30785
  return;
30758
30786
  }
30759
30787
  let raw;
30760
- try {
30761
- raw = await readFile(app.ui.path, "utf8");
30762
- } catch (err) {
30763
- res.writeHead(500, { "content-type": "application/json" });
30764
- res.end(
30765
- JSON.stringify({
30766
- error: `could not read app "${appId}"'s ui html at "${app.ui.path}": ${err instanceof Error ? err.message : String(err)}`
30767
- })
30768
- );
30769
- return;
30788
+ if (app?.ui) {
30789
+ try {
30790
+ raw = await readFile(app.ui.path, "utf8");
30791
+ } catch (err) {
30792
+ res.writeHead(500, { "content-type": "application/json" });
30793
+ res.end(
30794
+ JSON.stringify({
30795
+ error: `could not read app "${appId}"'s ui html at "${app.ui.path}": ${err instanceof Error ? err.message : String(err)}`
30796
+ })
30797
+ );
30798
+ return;
30799
+ }
30800
+ } else {
30801
+ raw = builtin.html;
30770
30802
  }
30771
30803
  const embedRequested = new URL(req.url ?? "/", "http://localhost").searchParams.get("embed") === "1";
30772
30804
  const headers = {
30773
30805
  "content-type": "text/html; charset=utf-8",
30774
30806
  "cache-control": "no-store"
30775
30807
  };
30776
- if (!(embedRequested && iframeEmbedAllowed(req, app))) {
30777
- headers["x-frame-options"] = "DENY";
30778
- headers["content-security-policy"] = "frame-ancestors 'none'";
30808
+ if (!(embedRequested && iframeEmbedAllowed(req, app ?? {}))) {
30809
+ headers["content-security-policy"] = `frame-ancestors 'self' ${frameAncestors.join(" ")}`.trimEnd();
30810
+ if (frameAncestors.length === 0) {
30811
+ headers["x-frame-options"] = "SAMEORIGIN";
30812
+ }
30779
30813
  }
30780
30814
  res.writeHead(200, headers);
30781
30815
  res.end(injectStandaloneAppBridge(raw));
@@ -30911,7 +30945,12 @@ async function handleAppUiToolCall(req, res, appId, appRegistry, deps2) {
30911
30945
  tool = inner.tool;
30912
30946
  args = inner.args && typeof inner.args === "object" && !Array.isArray(inner.args) ? inner.args : {};
30913
30947
  }
30914
- const result = await performAppToolCall(appRegistry, { appId, tool, args }, deps2);
30948
+ const installed = appRegistry.getApp(appId);
30949
+ const result = installed?.ui ? await performAppToolCall(appRegistry, { appId, tool, args }, deps2) : await performBuiltinPanelToolCall(
30950
+ resolveBuiltinPanelUi(appId, requestHttpBaseUrl(req))?.tools,
30951
+ { appId, tool, args },
30952
+ deps2
30953
+ );
30915
30954
  res.writeHead(200, { "content-type": "application/json" });
30916
30955
  res.end(JSON.stringify(result));
30917
30956
  }