@hosty-sdk/app 0.35.1 → 0.37.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/README.md CHANGED
@@ -298,6 +298,30 @@ PCM16 WAV. Browser microphone consent is separate from Core permission.
298
298
  credentials and the acting user's access. It does not expose general agent execution.
299
299
  See [Provider consumption](../../docs/features/provider-consumption/feature.md) for the full contract.
300
300
 
301
+ ## Tasks And Execution Providers
302
+
303
+ Core 0.159.0 and SDK 0.36.0 support independent `tasks` and `task-executor` categories. Request
304
+ `providers.tasks` or `providers.task-executor`, select an app/installation/key explicitly and use
305
+ server-only clients. Models, credentials and Harness profile internals are not shared task fields.
306
+
307
+ ```ts
308
+ import { ProviderClient } from "@hosty-sdk/app/providers/server";
309
+ import { TaskClient, TaskExecutorClient } from "@hosty-sdk/app/tasks/server";
310
+ const providers = new ProviderClient();
311
+ const tasks = new TaskClient(providers, selectedTaskProvider, getCurrentUserToken);
312
+ const draft = await tasks.create({ requestId, input: {
313
+ title: "Summarize the meeting", markdown: "List decisions and next steps.",
314
+ assignee: { kind: "app", executor: selectedExecutor, optionId }, resources: [],
315
+ } });
316
+ await tasks.action(draft.ref, { requestId: readyRequestId, expectedRevision: draft.revision, action: "ready" });
317
+ const executions = new TaskExecutorClient(providers, selectedExecutor, getCurrentUserToken);
318
+ const receipt = await executions.get(executionId);
319
+ ```
320
+
321
+ Retain each UUID and exact body after a timeout. Clients do not retry mutations implicitly. A task
322
+ provider persists execution intent before PUT; completed model output alone is not a task result.
323
+ See [Basic Tasks](../../docs/features/basic-tasks/feature.md) for the protocol and authority boundaries.
324
+
301
325
  ### Assistant MCP credentials
302
326
 
303
327
  MCP handlers use `introspectMcpToken` from `@hosty-sdk/app/scoped-token` for online validation of
@@ -572,12 +596,19 @@ See [UI composition and browser acceptance](../../docs/features/sdk-ui-compositi
572
596
 
573
597
  `@hosty-sdk/app/sandbox/react` exports `CoreEnvironmentPanel` for operator-facing child Core
574
598
  management. Supply an authenticated `EnvironmentRequest` transport, a tab-local replay storage key
575
- and optionally a workspace with its active worktrees. Shell and Workspaces use this shared panel
576
- for create/start, current/source Core selection, normal test-account setup, app status, preview and
577
- confirmed deletion. The component does not grant authority: Core independently checks the optional
599
+ and optionally a workspace with its active worktrees. It supports create/start, current/source
600
+ Core selection, normal test-account setup, app status, preview and confirmed deletion. Shell has
601
+ its own combined Core/environment rows and current-mode creation dialog. The component does not grant authority: Core independently checks the optional
578
602
  `core.environments.inspect/operate` grants, administrator identity and workspace source access.
579
603
 
580
- `@hosty-sdk/app/sandbox/server` provides the server-side clients for independent app sandboxes and
581
- child Core environments. Caller-owned `core.environments.read/manage` authority remains distinct
604
+ `@hosty-sdk/app/sandbox/server` provides CoreEnvironmentClient for child Core environments. Caller-owned `core.environments.read/manage` authority remains distinct
582
605
  from the operator panel's cross-caller permissions. See the
583
- [sandbox contract](../../docs/features/app-sandbox-runtimes/feature.md) for lifecycle and cleanup rules.
606
+ [environment contract](../../docs/features/app-sandbox-runtimes/feature.md) for lifecycle and cleanup rules.
607
+
608
+ Version 0.37 removes SandboxClient, SandboxAction, the single-app Sandbox* request/view types,
609
+ SandboxIdentity/SandboxTarget, readSandboxIdentity and readSandboxTarget. No relay identity or
610
+ HOSTY_SANDBOX_* protocol is supported. Use CoreEnvironmentClient plus ordinary child app/user APIs;
611
+ apps run with the same normal identity and app-owned roles as any other Core installation. Existing
612
+ core.environments.* grants require their ordinary review; removed apps.sandboxes.* grants confer
613
+ no replacement authority. The /sandbox/server and /sandbox/react module paths remain for the
614
+ retained child-environment API.
package/dist/index.d.ts CHANGED
@@ -206,8 +206,3 @@ export declare function decideRecoveryAction(input: {
206
206
  }): RecoveryAction;
207
207
  /** Paint-time navigation policy. Kept in the pure entry point for Node and edge consumers. */
208
208
  export declare function chromeBootstrapScript(defaults?: import("./ui-types").UiChromeOverrides): string;
209
- /** Execution scope is independent of the principal's role. Never infer it from an app ID. */
210
- import type { SandboxTarget, SandboxIdentityContext } from "./sandbox-identity.js";
211
- export type { SandboxTarget, SandboxIdentityContext } from "./sandbox-identity.js";
212
- export declare function readSandboxTarget(environment: Record<string, string | undefined>): SandboxTarget | null;
213
- export declare function readSandboxIdentity(payload: Record<string, unknown>, expected: SandboxTarget | null): SandboxIdentityContext | null;
package/dist/index.js CHANGED
@@ -269,24 +269,3 @@ export function chromeBootstrapScript(defaults = {}) {
269
269
  throw new Error("Invalid navigation default.");
270
270
  return `(()=>{try{const d=${JSON.stringify({ navigation })},p=new URLSearchParams(location.search),h=new URLSearchParams();for(const k of ["hosty_navigation","hosty_tools","hosty_tool_launcher","hosty_launch"])for(const v of p.getAll(k))h.append(k,v);document.documentElement.setAttribute('data-hosty-chrome-hints',h.toString());const one=k=>p.getAll(k).length===1?p.get(k):null,n=one('hosty_navigation'),l=one('hosty_launch');document.documentElement.setAttribute('data-hosty-navigation',n==='show'||n==='hide'?n:d.navigation??(l==='native'||l==='embedded'?'hide':'show'));}catch{}})();`;
271
271
  }
272
- export function readSandboxTarget(environment) {
273
- const fields = [environment.HOSTY_SANDBOX_INSTANCE_ID, environment.HOSTY_SANDBOX_GENERATION, environment.HOSTY_SANDBOX_DATA_REVISION];
274
- if (!environment.HOSTY_SANDBOX_PROTOCOL && fields.every(value => !value))
275
- return null;
276
- if (environment.HOSTY_SANDBOX_PROTOCOL !== "hosty-sandbox-v1" || fields.some(value => !/^[a-f0-9]{32}$/.test(value ?? "")))
277
- throw new Error("The sandbox execution target is missing or unsupported.");
278
- return { runtimeInstanceId: fields[0], generation: fields[1], dataRevision: fields[2] };
279
- }
280
- export function readSandboxIdentity(payload, expected) {
281
- if (!expected) {
282
- if (payload.identityRealm === "sandbox" || payload.runtimeInstanceId != null || payload.generation != null || payload.dataRevision != null)
283
- throw new Error("An installed app cannot accept a sandbox identity.");
284
- return null;
285
- }
286
- if (payload.runtimeInstanceId !== expected.runtimeInstanceId || payload.generation !== expected.generation || payload.dataRevision !== expected.dataRevision
287
- || (payload.identityRealm !== "sandbox" && payload.identityRealm !== "core") || typeof payload.appRole !== "string"
288
- || !Array.isArray(payload.permissions) || !payload.permissions.every(value => typeof value === "string")
289
- || (payload.identityRealm === "sandbox" && payload.hostRole != null))
290
- throw new Error("The identity belongs to a different or unsupported sandbox target.");
291
- return { ...expected, identityRealm: payload.identityRealm, appRole: payload.appRole, permissions: payload.permissions };
292
- }
@@ -7,7 +7,7 @@ import { useAppActivity } from "./activity-react.js";
7
7
  import { APP_SESSION_ENDED, APP_SESSION_RESTORED, APP_ACTIVITY_RENEWED, acceptAppAuthProtocol } from "./browser-auth.js";
8
8
  import { buildCoreOpenUrl, readRecoveryParams } from "./index.js";
9
9
  import { permissionReviewUrl, requestPermissionReview } from "./permissions.js";
10
- import { SdkBrand, SdkChromeStyles, SdkLoader } from "./sdk-chrome.js";
10
+ import { SdkBrand, SdkChromeStyles, SdkIcon, SdkLoader } from "./sdk-chrome.js";
11
11
  import { HostyUiRoot } from "./ui-react.js";
12
12
  function snapshot(body) {
13
13
  const value = body;
@@ -19,6 +19,7 @@ function snapshot(body) {
19
19
  const openUrl = recovery.appId ? buildCoreOpenUrl(recovery.corePublicOrigin, recovery.appId, window.location) : null;
20
20
  // The activity policy remains authoritative even when renewal discovery fails.
21
21
  return { userId: value.userId, setup, notice: value.hosty?.notice,
22
+ permissionWarning: value.hosty?.permissionWarning === "unsupported",
22
23
  activityRequired: value.activityRequired === true,
23
24
  activeUntil: typeof value.activeUntil === "string" ? value.activeUntil : null,
24
25
  recoveryReady: !!openUrl && !!acceptAppAuthProtocol(new URL(openUrl).origin, recovery.appAuthProtocol) };
@@ -189,7 +190,7 @@ export function HostyOverlay({ children, probePath = "/api/hosty/session", appCo
189
190
  }
190
191
  const localOffline = (identity.ui.kind === "unavailable" || (identity.ui.kind === "active" && session?.setup === "unavailable"))
191
192
  && localContent !== undefined;
192
- return _jsxs(_Fragment, { children: [localOffline && _jsxs("div", { "data-hosty-local-content": true, children: [_jsxs("div", { "data-hosty-sdk": true, className: "hosty-overlay-offline", role: "status", children: [_jsx(SdkChromeStyles, {}), _jsx("style", { children: styles }), _jsx(SdkBrand, {}), _jsx("span", { children: "Hosty is unavailable. Local content remains available." }), _jsx("button", { type: "button", className: "hosty-sdk-button", onClick: () => { void refreshRef.current(); }, children: "Retry" })] }), localContent] }), _jsx("div", { "data-hosty-content": true, hidden: !ready, inert: !ready, style: ready ? { display: "contents" } : { display: "none" }, children: _jsx(HostyUiRoot, { chrome: chrome, navigation: navigation, uiPath: uiPath, panelLayout: panelLayout, active: ready, children: mounted.current ? children : null }) }), !ready && !localOffline && _jsx(OverlaySurface, { pending: pending, title: title, message: message, action: action, actionLabel: actionLabel, reviewUrl: reviewUrl })] });
193
+ return _jsxs(_Fragment, { children: [ready && session?.permissionWarning && _jsxs("div", { "data-hosty-sdk": true, role: "status", className: "hosty-overlay-offline", children: [_jsx(SdkChromeStyles, {}), _jsx("style", { children: styles }), _jsx(SdkIcon, { name: "warning" }), _jsxs("span", { children: ["This Core does not support some permissions requested by this app. Update the app or Core. These permissions are not granted; some app features may not work.", session.notice?.permissions?.unsupportedRequired?.length ? ` (${session.notice.permissions.unsupportedRequired.join(", ")})` : ""] })] }), localOffline && _jsxs("div", { "data-hosty-local-content": true, children: [_jsxs("div", { "data-hosty-sdk": true, className: "hosty-overlay-offline", role: "status", children: [_jsx(SdkChromeStyles, {}), _jsx("style", { children: styles }), _jsx(SdkBrand, {}), _jsx("span", { children: "Hosty is unavailable. Local content remains available." }), _jsx("button", { type: "button", className: "hosty-sdk-button", onClick: () => { void refreshRef.current(); }, children: "Retry" })] }), localContent] }), _jsx("div", { "data-hosty-content": true, hidden: !ready, inert: !ready, style: ready ? { display: "contents" } : { display: "none" }, children: _jsx(HostyUiRoot, { chrome: chrome, navigation: navigation, uiPath: uiPath, panelLayout: panelLayout, active: ready, children: mounted.current ? children : null }) }), !ready && !localOffline && _jsx(OverlaySurface, { pending: pending, title: title, message: message, action: action, actionLabel: actionLabel, reviewUrl: reviewUrl })] });
193
194
  }
194
195
  function OverlaySurface({ pending, title, message, action, actionLabel, reviewUrl }) {
195
196
  const [portal, setPortal] = useState(null);
@@ -42,9 +42,7 @@ export function MissingPermissionsNotice({ endpoint = "/api/hosty/permissions" }
42
42
  const kind = state && permissionNotice(state);
43
43
  if (dismissed || !kind || !state)
44
44
  return null;
45
- return _jsxs("section", { "data-hosty-sdk": true, "data-hosty-activity-control": true, role: "alert", "aria-label": "Required permissions", className: "hosty-permissions-notice", children: [_jsx(SdkChromeStyles, {}), _jsx("style", { children: styles }), _jsxs("header", { children: [_jsx(SdkBrand, {}), _jsx(SdkIcon, { name: "warning" }), _jsx("strong", { children: "Permissions needed" })] }), kind === "unsupported"
46
- ? _jsxs("p", { children: ["Core does not support required permissions: ", state.permissions.unsupportedRequired.join(", "), ". Update the app or Core."] })
47
- : _jsx("p", { children: "This app needs required permissions. Operations using them are unavailable until approval." }), kind === "missing" && state.permissions?.reviewAvailable !== false && state.corePublicOrigin &&
45
+ return _jsxs("section", { "data-hosty-sdk": true, "data-hosty-activity-control": true, role: "alert", "aria-label": "Required permissions", className: "hosty-permissions-notice", children: [_jsx(SdkChromeStyles, {}), _jsx("style", { children: styles }), _jsxs("header", { children: [_jsx(SdkBrand, {}), _jsx(SdkIcon, { name: "warning" }), _jsx("strong", { children: "Permissions needed" })] }), !!state.permissions?.unsupportedRequired?.length && _jsxs("p", { children: ["This Core does not support requested permissions: ", state.permissions.unsupportedRequired.join(", "), ". Update the app or Core. These permissions are not granted; some app features may not work."] }), kind === "missing" && _jsx("p", { children: "This app needs required permissions. Operations using them are unavailable until approval." }), kind === "missing" && state.permissions?.reviewAvailable !== false && state.corePublicOrigin &&
48
46
  _jsx("button", { type: "button", className: "hosty-sdk-button", onClick: () => { requestPermissionReview(state); setPending(true); }, children: "Request permissions" }), pending && _jsxs("p", { className: "hosty-sdk-status", children: [_jsx(SdkSpinner, {}), "Review permissions in Core, then return here. You can leave this app installed without approving."] }), _jsx("button", { type: "button", onClick: () => { setDismissed(true); setPending(false); }, className: "hosty-sdk-button hosty-sdk-ghost", children: "Dismiss" })] });
49
47
  }
50
48
  const styles = `
@@ -4,9 +4,9 @@ export function permissionNotice(state) {
4
4
  if (state.hostRole !== "host.admin" || !state.permissions)
5
5
  return null;
6
6
  const p = state.permissions;
7
- if (p.unsupportedRequired?.length)
8
- return "unsupported";
9
- return p.required.some(name => !p.granted.includes(name)) ? "missing" : null;
7
+ if (p.required.some(name => !p.unsupportedRequired?.includes(name) && !p.granted.includes(name)))
8
+ return "missing";
9
+ return p.unsupportedRequired?.length ? "unsupported" : null;
10
10
  }
11
11
  export function permissionReviewUrl(corePublicOrigin, appId) {
12
12
  const core = new URL(corePublicOrigin);
@@ -30,6 +30,8 @@ export declare class ProviderClient {
30
30
  assistant(provider: Pick<ProviderDescriptor, "appId" | "key"> & {
31
31
  installationId?: string | null;
32
32
  }, userToken: () => Promise<string>): Promise<AssistantClient>;
33
+ /** Bounded task protocol transport. The selected installation is checked on every operation. */
34
+ taskRequest<T>(provider: import("./tasks.js").TaskProviderRef, kind: "tasks" | "task-executor", route: string, method: string, input?: unknown, userToken?: () => Promise<string>, signal?: AbortSignal): Promise<T>;
33
35
  /** Provider-side validation checks live grants on every request; never cache this result. */
34
36
  validate(token: string, kind: ProviderKind, key?: string, signal?: AbortSignal): Promise<ProviderInvocation>;
35
37
  }
@@ -79,6 +79,19 @@ export class ProviderClient {
79
79
  return { token: fresh.token };
80
80
  });
81
81
  }
82
+ /** Bounded task protocol transport. The selected installation is checked on every operation. */
83
+ async taskRequest(provider, kind, route, method, input, userToken, signal) {
84
+ const allowed = kind === "tasks"
85
+ ? /^(?:\/capabilities|\/tasks(?:\?[a-zA-Z0-9%=&_-]*|\/[a-zA-Z0-9-]+(?:\/actions)?)?)$/
86
+ : /^(?:\/options|\/executions\/[a-zA-Z0-9-]+(?:\/cancel)?)$/;
87
+ if (!allowed.test(route) || !["GET", "POST", "PATCH", "PUT"].includes(method))
88
+ throw new ProviderError("task_route_invalid", "Use a version-one task operation.", 400);
89
+ const binding = await this.connect({ ...provider, kind }, signal, await userToken?.());
90
+ return read(await fetch(`${binding.url}${route}`, { method, redirect: "error", cache: "no-store",
91
+ signal: signal ? AbortSignal.any([signal, AbortSignal.timeout(15_000)]) : AbortSignal.timeout(15_000),
92
+ headers: { authorization: `Bearer ${binding.token}`, "content-type": "application/json" },
93
+ body: input === undefined ? undefined : JSON.stringify(input) }));
94
+ }
82
95
  /** Provider-side validation checks live grants on every request; never cache this result. */
83
96
  validate(token, kind, key = "default", signal) {
84
97
  return this.coreRequest("/provider/introspect", { token, kind, key }, signal);
@@ -1,5 +1,5 @@
1
- /** Provider categories supported by Core. Agent execution is intentionally not part of this contract. */
2
- export type ProviderKind = "speech-to-text" | "assistant" | "translation";
1
+ /** Provider categories supported by Core. Task execution does not expose model/profile configuration. */
2
+ export type ProviderKind = "speech-to-text" | "assistant" | "translation" | "tasks" | "task-executor";
3
3
  export type ProviderDescriptor = {
4
4
  appId: string;
5
5
  displayName: string;
@@ -1,39 +1,9 @@
1
1
  import type { ProviderServerOptions } from "./providers-server.js";
2
- export type SandboxSource = {
3
- kind: "worktree";
4
- worktreeId: string;
5
- } | {
6
- kind: "appArtifact";
7
- artifactDigest?: string;
8
- };
9
- export interface SandboxLimits {
10
- memoryBytes?: number;
11
- cpus?: number;
12
- pids?: number;
13
- dataBytes?: number;
14
- cacheBytes?: number;
15
- startupSeconds?: number;
16
- }
17
2
  export interface SandboxContext {
18
3
  workspaceId?: string;
19
4
  sessionId?: string;
20
5
  jobId?: string;
21
6
  }
22
- export interface SandboxPlanInput {
23
- appId: string;
24
- profile: string;
25
- source: SandboxSource;
26
- limits?: SandboxLimits;
27
- context?: SandboxContext;
28
- }
29
- export interface SandboxPlan {
30
- id: string;
31
- digest: string;
32
- inputDigest: string;
33
- request: SandboxPlanInput;
34
- blockers: string[];
35
- expiresAt: string;
36
- }
37
7
  export interface SandboxOperation {
38
8
  id: string;
39
9
  kind: string;
@@ -41,96 +11,11 @@ export interface SandboxOperation {
41
11
  errorCode?: string;
42
12
  message?: string;
43
13
  }
44
- export interface SandboxUser {
45
- id: string;
46
- fixtureKey: string;
47
- displayName: string;
48
- role: string;
49
- permissions: string[];
50
- enabled: boolean;
51
- revision: number;
52
- }
53
- export interface SandboxInstance {
54
- id: string;
55
- appId: string;
56
- source: SandboxSource;
57
- context?: SandboxContext;
58
- revision: number;
59
- generation: string;
60
- dataRevision: string;
61
- state: string;
62
- previewUrl?: string;
63
- errorCode?: string;
64
- operations: SandboxOperation[];
65
- users: SandboxUser[];
66
- viewers?: string[];
67
- endpoints?: {
68
- key: string;
69
- url: string;
70
- }[];
71
- metrics?: {
72
- at: string;
73
- memoryBytes: number | null;
74
- sampledPeakMemoryBytes: number | null;
75
- services: {
76
- service: string;
77
- memoryBytes: number | null;
78
- cpuPercent: number | null;
79
- }[];
80
- };
81
- storage?: {
82
- at: string;
83
- dataBytes: number;
84
- seedBytes: number;
85
- cacheBytes: number;
86
- evidenceBytes: number;
87
- totalBytes: number;
88
- warning: boolean;
89
- errorCode?: string;
90
- };
91
- }
92
- export interface SandboxDiagnostics {
93
- instanceId: string;
94
- state: string;
95
- errorCode?: string;
96
- services: {
97
- service: string;
98
- state: string;
99
- text: string;
100
- }[];
101
- }
102
- export declare const sandboxActions: readonly ["sandbox-logs", "sandbox-plan", "sandbox-prepare", "sandbox-list", "sandbox-status", "sandbox-users", "sandbox-seed", "sandbox-start", "sandbox-restart", "sandbox-stop", "sandbox-reset", "sandbox-dispose", "sandbox-browser", "sandbox-export", "sandbox-user-delete", "sandbox-viewers", "sandbox-evidence-clear", "sandbox-preview", "sandbox-browser-cancel"];
103
- export type SandboxAction = typeof sandboxActions[number];
104
14
  export declare class SandboxError extends Error {
105
15
  readonly code: string;
106
16
  readonly status: number | null;
107
17
  constructor(code: string, message: string, status: number | null);
108
18
  }
109
- /** Server-only transport. The authority callback can supply an interactive grant or an explicitly
110
- * approved BackgroundGrantClient.token(); neither workspaces nor assistant roles are required. */
111
- export declare class SandboxClient {
112
- private readonly authority;
113
- private readonly origin;
114
- private readonly appId;
115
- private readonly service;
116
- constructor(authority: () => Promise<string>, options?: ProviderServerOptions);
117
- plan(input: SandboxPlanInput): Promise<SandboxPlan>;
118
- prepare(input: {
119
- requestId: string;
120
- planId: string;
121
- digest: string;
122
- }): Promise<SandboxInstance>;
123
- list(): Promise<{
124
- instances: SandboxInstance[];
125
- }>;
126
- status(runtimeInstanceId: string): Promise<SandboxInstance>;
127
- command(runtimeInstanceId: string, action: "start" | "restart" | "stop" | "reset" | "dispose", input: {
128
- requestId: string;
129
- expectedRevision: number;
130
- seedRevision?: string;
131
- }): Promise<SandboxInstance>;
132
- call<T = unknown>(action: SandboxAction, input: Record<string, unknown>): Promise<T>;
133
- }
134
19
  export declare const coreEnvironmentActions: readonly ["environment-consent", "environment-create", "environment-list", "environment-status", "environment-start", "environment-stop", "environment-restart", "environment-finish", "environment-setup", "environment-api", "environment-initialization", "environment-initialization-resume", "environment-setup-link"];
135
20
  export type CoreEnvironmentAction = typeof coreEnvironmentActions[number];
136
21
  export interface CoreEnvironmentSavedConsent {
@@ -153,6 +38,7 @@ export interface CoreEnvironment {
153
38
  dataRoot?: string;
154
39
  previewUrl: string;
155
40
  initializationRequested?: boolean;
41
+ coreVersion?: string | null;
156
42
  process?: {
157
43
  pid: number;
158
44
  startedAt: string;
@@ -1,5 +1,3 @@
1
- export const sandboxActions = ["sandbox-logs", "sandbox-plan", "sandbox-prepare", "sandbox-list", "sandbox-status", "sandbox-users", "sandbox-seed",
2
- "sandbox-start", "sandbox-restart", "sandbox-stop", "sandbox-reset", "sandbox-dispose", "sandbox-browser", "sandbox-export", "sandbox-user-delete", "sandbox-viewers", "sandbox-evidence-clear", "sandbox-preview", "sandbox-browser-cancel"];
3
1
  export class SandboxError extends Error {
4
2
  code;
5
3
  status;
@@ -10,89 +8,6 @@ export class SandboxError extends Error {
10
8
  this.name = "SandboxError";
11
9
  }
12
10
  }
13
- /** Server-only transport. The authority callback can supply an interactive grant or an explicitly
14
- * approved BackgroundGrantClient.token(); neither workspaces nor assistant roles are required. */
15
- export class SandboxClient {
16
- authority;
17
- origin;
18
- appId;
19
- service;
20
- constructor(authority, options = {}) {
21
- this.authority = authority;
22
- this.origin = new URL(options.coreOrigin ?? process.env.HOSTY_CORE_ORIGIN ?? "http://127.0.0.1:7070");
23
- this.appId = options.appId ?? process.env.HOSTY_APP_ID ?? "";
24
- this.service = options.serviceToken ?? process.env.HOSTY_APP_SERVICE_TOKEN ?? "";
25
- if (!this.appId || !this.service || !["http:", "https:"].includes(this.origin.protocol) || this.origin.username || this.origin.password
26
- || this.origin.pathname !== "/" || this.origin.search || this.origin.hash)
27
- throw new Error("A valid Core origin and calling app service identity are required.");
28
- }
29
- plan(input) { return this.call("sandbox-plan", input); }
30
- prepare(input) { return this.call("sandbox-prepare", input); }
31
- list() { return this.call("sandbox-list", {}); }
32
- status(runtimeInstanceId) { return this.call("sandbox-status", { runtimeInstanceId }); }
33
- command(runtimeInstanceId, action, input) {
34
- return this.call(`sandbox-${action}`, { runtimeInstanceId, ...input });
35
- }
36
- async call(action, input) {
37
- if (!sandboxActions.includes(action))
38
- throw new SandboxError("sandbox_operation_invalid", "Unknown sandbox operation.", 400);
39
- const { runtimeInstanceId, ...body } = input;
40
- const jobId = body.jobId;
41
- const root = `/api/internal/apps/${encodeURIComponent(this.appId)}/sandbox-instances`;
42
- const read = action === "sandbox-logs" || action === "sandbox-list" || action === "sandbox-status" || action === "sandbox-export" || action === "sandbox-preview";
43
- let suffix = "";
44
- if (action === "sandbox-plan")
45
- suffix = "/plans";
46
- else if (action !== "sandbox-list" && action !== "sandbox-prepare") {
47
- if (typeof runtimeInstanceId !== "string" || !/^[a-f0-9]{32}$/.test(runtimeInstanceId))
48
- throw new SandboxError("sandbox_id_invalid", "Use the Core-issued runtime instance ID.", 400);
49
- suffix = `/${runtimeInstanceId}`;
50
- if (action === "sandbox-logs")
51
- suffix += "/diagnostics";
52
- else if (action === "sandbox-users")
53
- suffix += "/users";
54
- else if (action === "sandbox-user-delete")
55
- suffix += "/users/delete";
56
- else if (action === "sandbox-viewers")
57
- suffix += "/viewers";
58
- else if (action === "sandbox-evidence-clear")
59
- suffix += "/evidence/clear";
60
- else if (action === "sandbox-seed")
61
- suffix += "/seeds";
62
- else if (action === "sandbox-browser")
63
- suffix += "/browser-runs";
64
- else if (action === "sandbox-browser-cancel")
65
- suffix += "/browser-runs/cancel";
66
- else if (action === "sandbox-preview")
67
- suffix += "/preview" + (typeof body.userId === "string" ? "?userId=" + encodeURIComponent(body.userId) : "");
68
- else if (action === "sandbox-export") {
69
- if (typeof jobId !== "string" || !/^[a-f0-9]{32}$/.test(jobId))
70
- throw new SandboxError("sandbox_job_invalid", "Select a recorded browser run.", 400);
71
- suffix += `/browser-runs/${jobId}`;
72
- }
73
- else if (!read)
74
- suffix += `/operations/${action.slice("sandbox-".length)}`;
75
- }
76
- if (!read && action !== "sandbox-plan" && (typeof body.requestId !== "string" || !/^[a-f0-9-]{32,36}$/i.test(body.requestId)))
77
- throw new SandboxError("sandbox_request_required", "Persist a request UUID before sending a mutation. Replay the same UUID and arguments after an uncertain result.", 400);
78
- const credential = await this.authority();
79
- let response;
80
- try {
81
- response = await fetch(new URL(root + suffix, this.origin), { method: read ? "GET" : "POST", redirect: "error", cache: "no-store",
82
- headers: { authorization: `Bearer ${this.service}`, "X-Hosty-User-Token": credential, "content-type": "application/json" },
83
- body: read ? undefined : JSON.stringify(body), signal: AbortSignal.timeout(action === "sandbox-browser" ? 330_000 : 180_000) });
84
- }
85
- catch {
86
- throw new SandboxError("sandbox_outcome_unknown", "Core could not confirm the outcome. Read the operation or replay its original request UUID and arguments.", null);
87
- }
88
- const result = await response.json().catch(() => null);
89
- if (!result)
90
- throw new SandboxError("sandbox_outcome_unknown", "Core returned no readable outcome; keep the original operation identity.", response.status);
91
- if (!response.ok)
92
- throw new SandboxError(result.code ?? "sandbox_failed", result.message ?? "Sandbox request failed.", response.status);
93
- return result;
94
- }
95
- }
96
11
  export const coreEnvironmentActions = ["environment-consent", "environment-create", "environment-list", "environment-status", "environment-start",
97
12
  "environment-stop", "environment-restart", "environment-finish", "environment-setup", "environment-api", "environment-initialization", "environment-initialization-resume", "environment-setup-link"];
98
13
  /** Full child Core transport. Requires core.environments permissions and live administrator authority.
package/dist/server.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import "server-only";
2
- import { type SandboxIdentityContext } from "./index.js";
3
2
  import { type AppContinuity } from "./continuity.js";
4
3
  import { type AppAuthProtocol } from "./app-code.js";
5
4
  export { isValidCodeVerifier } from "./app-code.js";
@@ -28,8 +27,6 @@ export interface HostIdentity {
28
27
  activeUntil?: string | null;
29
28
  activityRequired?: boolean;
30
29
  continuity?: AppContinuity;
31
- /** Present for every identity (including real viewers) acting inside a sandbox. */
32
- sandbox?: SandboxIdentityContext;
33
30
  }
34
31
  /** Structured detail for a failed resolution: the Core HTTP status (null when Core was
35
32
  * never reached) plus a machine-readable code and human-readable message. Codes pass
package/dist/server.js CHANGED
@@ -1,7 +1,6 @@
1
1
  // Server slice: Next route-handler factories and Core revalidation. Never reaches a client
2
2
  // bundle — the service token lives here.
3
3
  import "server-only";
4
- import { readSandboxTarget, readSandboxIdentity } from "./index.js";
5
4
  import { readAppContinuity } from "./continuity.js";
6
5
  import { createHostySessionResponse } from "./session-server.js";
7
6
  import { createHostyUiResponse } from "./ui-server.js";
@@ -68,8 +67,6 @@ export function readAppIdentityToken(headers, config) {
68
67
  return token;
69
68
  }
70
69
  }
71
- if (process.env.HOSTY_SANDBOX_PROTOCOL)
72
- return headers.get(HOSTY_APP_IDENTITY_HEADER)?.trim() || readCookie(headers.get("cookie"), config.identityCookieName) || null;
73
70
  return readCookie(headers.get("cookie"), config.identityCookieName) || headers.get(HOSTY_APP_IDENTITY_HEADER)?.trim() || null;
74
71
  }
75
72
  /** True when the effective request protocol is https (honouring the forwarded proto). */
@@ -122,8 +119,7 @@ export async function resolveAppSession(token, config) {
122
119
  };
123
120
  }
124
121
  const now = Date.now();
125
- // Synthetic subject changes and instance revocation are checked online on every request.
126
- const cached = process.env.HOSTY_SANDBOX_PROTOCOL ? undefined : revalidationCache.get(token);
122
+ const cached = revalidationCache.get(token);
127
123
  if (cached) {
128
124
  if (now - cached.cachedAt < REVALIDATION_CACHE_TTL_MS && now < cached.expiresAtMs) {
129
125
  return { status: "active", identity: cached.identity };
@@ -215,13 +211,6 @@ async function revalidateWithCore(token, coreOrigin, serviceToken, config) {
215
211
  if (!payload || payload.active !== true || !userId) {
216
212
  return { status: "expired" };
217
213
  }
218
- let sandbox;
219
- try {
220
- sandbox = readSandboxIdentity(payload, readSandboxTarget(process.env));
221
- }
222
- catch {
223
- return { status: "forbidden", error: { status: 403, code: "sandbox_identity_mismatch", message: "The identity does not match this execution target." } };
224
- }
225
214
  const hostRole = readString(payload.hostRole);
226
215
  const expiresAt = readString(payload.expiresAt);
227
216
  const identity = {
@@ -229,15 +218,14 @@ async function revalidateWithCore(token, coreOrigin, serviceToken, config) {
229
218
  email: readString(payload.email),
230
219
  displayName: readString(payload.displayName),
231
220
  hostRole,
232
- appRole: sandbox?.identityRealm === "sandbox" ? sandbox.appRole : config.mapHostRole ? config.mapHostRole(hostRole) : hostRole,
221
+ appRole: config.mapHostRole ? config.mapHostRole(hostRole) : hostRole,
233
222
  expiresAt,
234
223
  activeUntil: readString(payload.activeUntil),
235
224
  activityRequired: payload.activityRequired === true,
236
225
  continuity: readAppContinuity(payload.continuity),
237
- ...(sandbox ? { sandbox } : {}),
238
226
  };
239
227
  const expiresAtMs = expiresAt ? Date.parse(expiresAt) : Number.NaN;
240
- if (!sandbox && Number.isFinite(expiresAtMs) && expiresAtMs > Date.now()) {
228
+ if (Number.isFinite(expiresAtMs) && expiresAtMs > Date.now()) {
241
229
  pruneRevalidationCache();
242
230
  revalidationCache.set(token, { identity, expiresAtMs, cachedAt: Date.now() });
243
231
  }
@@ -1,6 +1,5 @@
1
1
  import { type ProviderServerOptions } from "@hosty-sdk/app/providers/server";
2
2
  import type { AppSessionStatus, SessionRecoveryParams } from "./session-types.js";
3
- import type { SandboxIdentityContext } from "./sandbox-identity.js";
4
3
  import type { AppContinuity } from "./continuity.js";
5
4
  /** Framework-neutral server adapter. Call with an already authenticated app session, never request data. */
6
5
  export type HostySessionInput = {
@@ -11,7 +10,6 @@ export type HostySessionInput = {
11
10
  activeUntil?: string | null;
12
11
  activityRequired?: boolean;
13
12
  continuity?: AppContinuity;
14
- sandbox?: SandboxIdentityContext;
15
13
  };
16
14
  error?: {
17
15
  code: string;
@@ -7,20 +7,15 @@ export async function createHostySessionResponse(session, recovery, options = {}
7
7
  const identity = session.identity;
8
8
  if (options.administratorOnly && identity.hostRole !== "host.admin")
9
9
  return Response.json({ status: "forbidden", recovery }, { headers });
10
- if (identity.sandbox) {
11
- // Core admits only recipes without required global permissions in this offline profile.
12
- // The revalidated execution identity is authoritative; never query the installed app's
13
- // permission state or advertise a global permission-review action from a test realm.
14
- return Response.json({ status: "active", ...identity, recovery, hosty: { version: 1, setup: "ready" } }, { headers });
15
- }
16
10
  let setup = "unavailable";
11
+ let permissionWarning;
17
12
  let notice;
18
13
  try {
19
14
  const permissions = await new ProviderClient({ ...options, appId: recovery.appId ?? undefined }).permissions(options.signal);
20
15
  if (![permissions.required, permissions.optional, permissions.granted, permissions.unsupportedRequired ?? []].every(list => Array.isArray(list) && list.every(name => typeof name === "string")))
21
16
  throw new Error("Invalid permission state.");
22
- setup = permissions.unsupportedRequired?.length ? "unsupported"
23
- : permissions.required.some(name => !permissions.granted.includes(name)) ? "missing" : "ready";
17
+ setup = permissions.required.some(name => !permissions.unsupportedRequired?.includes(name) && !permissions.granted.includes(name)) ? "missing" : "ready";
18
+ permissionWarning = permissions.unsupportedRequired?.length ? "unsupported" : undefined;
24
19
  if (identity.hostRole === "host.admin")
25
20
  notice = { hostRole: identity.hostRole, appId: recovery.appId ?? "",
26
21
  corePublicOrigin: recovery.corePublicOrigin, permissions };
@@ -29,5 +24,5 @@ export async function createHostySessionResponse(session, recovery, options = {}
29
24
  if (error instanceof ProviderError && error.status === 404)
30
25
  setup = "incompatible";
31
26
  }
32
- return Response.json({ status: "active", ...identity, recovery, hosty: { version: 1, setup, notice } }, { headers });
27
+ return Response.json({ status: "active", ...identity, recovery, hosty: { version: 1, setup, permissionWarning, notice } }, { headers });
33
28
  }
@@ -0,0 +1,33 @@
1
+ import type { ProviderClient } from "./providers-server.js";
2
+ import type { TaskProviderRef, TaskRef, TaskRecord, TaskPage, TaskCreate, TaskEdit, TaskAction, TaskStatus, TaskCapabilities, TaskExecutorOptions, TaskExecutionInput, TaskExecutionReceipt } from "./tasks.js";
3
+ /** Server-side task API. Every request obtains a fresh Core binding; no mutation is retried implicitly. */
4
+ export declare class TaskClient {
5
+ private readonly providers;
6
+ private readonly provider;
7
+ private readonly userToken?;
8
+ constructor(providers: ProviderClient, provider: TaskProviderRef, userToken?: (() => Promise<string>) | undefined);
9
+ private call;
10
+ private id;
11
+ capabilities(signal?: AbortSignal): Promise<TaskCapabilities>;
12
+ list(options?: {
13
+ cursor?: string;
14
+ limit?: number;
15
+ status?: TaskStatus;
16
+ signal?: AbortSignal;
17
+ }): Promise<TaskPage>;
18
+ create(input: TaskCreate, signal?: AbortSignal): Promise<TaskRecord>;
19
+ get(ref: TaskRef, signal?: AbortSignal): Promise<TaskRecord>;
20
+ edit(ref: TaskRef, input: TaskEdit, signal?: AbortSignal): Promise<TaskRecord>;
21
+ action(ref: TaskRef, input: TaskAction, signal?: AbortSignal): Promise<TaskRecord>;
22
+ }
23
+ export declare class TaskExecutorClient {
24
+ private readonly providers;
25
+ private readonly executor;
26
+ private readonly userToken?;
27
+ constructor(providers: ProviderClient, executor: TaskProviderRef, userToken?: (() => Promise<string>) | undefined);
28
+ private call;
29
+ options(signal?: AbortSignal): Promise<TaskExecutorOptions>;
30
+ accept(executionId: string, input: TaskExecutionInput, signal?: AbortSignal): Promise<TaskExecutionReceipt>;
31
+ get(executionId: string, signal?: AbortSignal): Promise<TaskExecutionReceipt>;
32
+ cancel(executionId: string, requestId: string, signal?: AbortSignal): Promise<TaskExecutionReceipt>;
33
+ }
@@ -0,0 +1,57 @@
1
+ /** Server-side task API. Every request obtains a fresh Core binding; no mutation is retried implicitly. */
2
+ export class TaskClient {
3
+ providers;
4
+ provider;
5
+ userToken;
6
+ constructor(providers, provider, userToken) {
7
+ this.providers = providers;
8
+ this.provider = provider;
9
+ this.userToken = userToken;
10
+ }
11
+ call(route, method = "GET", input, signal) {
12
+ return this.providers.taskRequest(this.provider, "tasks", route, method, input, this.userToken, signal);
13
+ }
14
+ id(ref) {
15
+ if (ref.appId !== this.provider.appId || ref.installationId !== this.provider.installationId || ref.key !== this.provider.key)
16
+ throw new Error("The task belongs to another provider installation.");
17
+ return encodeURIComponent(ref.id);
18
+ }
19
+ capabilities(signal) { return this.call("/capabilities", "GET", undefined, signal); }
20
+ list(options = {}) {
21
+ const query = new URLSearchParams();
22
+ if (options.cursor)
23
+ query.set("cursor", options.cursor);
24
+ if (options.limit !== undefined)
25
+ query.set("limit", String(options.limit));
26
+ if (options.status)
27
+ query.set("status", options.status);
28
+ return this.call(`/tasks?${query}`, "GET", undefined, options.signal);
29
+ }
30
+ create(input, signal) { return this.call("/tasks", "POST", input, signal); }
31
+ get(ref, signal) { return this.call(`/tasks/${this.id(ref)}`, "GET", undefined, signal); }
32
+ edit(ref, input, signal) { return this.call(`/tasks/${this.id(ref)}`, "PATCH", input, signal); }
33
+ action(ref, input, signal) { return this.call(`/tasks/${this.id(ref)}/actions`, "POST", input, signal); }
34
+ }
35
+ export class TaskExecutorClient {
36
+ providers;
37
+ executor;
38
+ userToken;
39
+ constructor(providers, executor, userToken) {
40
+ this.providers = providers;
41
+ this.executor = executor;
42
+ this.userToken = userToken;
43
+ }
44
+ call(route, method = "GET", input, signal) {
45
+ return this.providers.taskRequest(this.executor, "task-executor", route, method, input, this.userToken, signal);
46
+ }
47
+ options(signal) { return this.call("/options", "GET", undefined, signal); }
48
+ accept(executionId, input, signal) {
49
+ return this.call(`/executions/${encodeURIComponent(executionId)}`, "PUT", input, signal);
50
+ }
51
+ get(executionId, signal) {
52
+ return this.call(`/executions/${encodeURIComponent(executionId)}`, "GET", undefined, signal);
53
+ }
54
+ cancel(executionId, requestId, signal) {
55
+ return this.call(`/executions/${encodeURIComponent(executionId)}/cancel`, "POST", { requestId }, signal);
56
+ }
57
+ }
@@ -0,0 +1,148 @@
1
+ /** Core's version-one task contract. Numbers are opaque; references bind an installation. */
2
+ export type TaskProviderRef = {
3
+ appId: string;
4
+ installationId: string;
5
+ key: string;
6
+ };
7
+ export type TaskRef = TaskProviderRef & {
8
+ id: string;
9
+ };
10
+ export type TaskStatus = "Draft" | "Ready" | "InProgress" | "Blocked" | "Done" | "Cancelled";
11
+ export type TaskResource = {
12
+ kind: "app" | "workspace" | "external";
13
+ owner: string;
14
+ id: string;
15
+ label?: string;
16
+ version: {
17
+ mode: "live";
18
+ } | {
19
+ mode: "pinned";
20
+ revision: string;
21
+ };
22
+ };
23
+ export type TaskAssignee = {
24
+ kind: "human";
25
+ userId: string;
26
+ name?: string;
27
+ } | {
28
+ kind: "app";
29
+ executor: TaskProviderRef;
30
+ optionId?: string;
31
+ name?: string;
32
+ };
33
+ export type TaskInput = {
34
+ title: string;
35
+ markdown: string;
36
+ assignee: TaskAssignee | null;
37
+ requiresReview: boolean;
38
+ resources: TaskResource[];
39
+ workspaceId?: string;
40
+ };
41
+ export type TaskActor = {
42
+ userId: string | null;
43
+ appId: string | null;
44
+ installationId?: string;
45
+ };
46
+ export type TaskResult = {
47
+ markdown: string;
48
+ resources: TaskResource[];
49
+ };
50
+ export type TaskExecutionState = "accepted" | "running" | "awaiting_input" | "cancelling" | "completed" | "failed" | "cancelled" | "interrupted";
51
+ export type TaskExecutionInput = {
52
+ taskRef: TaskRef;
53
+ taskRevision: number;
54
+ executorOptionId?: string;
55
+ input: Omit<TaskInput, "assignee">;
56
+ };
57
+ export type TaskExecutionReceipt = {
58
+ executionId: string;
59
+ revision: number;
60
+ state: TaskExecutionState;
61
+ stopConfirmed: boolean;
62
+ createdAt: string;
63
+ updatedAt: string;
64
+ result?: TaskResult;
65
+ error?: {
66
+ code: string;
67
+ message: string;
68
+ }; /** An absolute HTTP(S) UI URL for cross-app readers; same-app readers can resolve a local path. */
69
+ sessionRef?: {
70
+ id: string;
71
+ path: string;
72
+ };
73
+ };
74
+ export type TaskAttempt = {
75
+ executionId: string;
76
+ executor: TaskProviderRef;
77
+ request: TaskExecutionInput;
78
+ receipt?: TaskExecutionReceipt;
79
+ cancelRequested: boolean;
80
+ createdAt: string;
81
+ };
82
+ export type TaskOutcome = {
83
+ inputRevision: number;
84
+ result: TaskResult;
85
+ recordedAt: string;
86
+ executionId?: string;
87
+ };
88
+ export type TaskRecord = TaskInput & {
89
+ ref: TaskRef;
90
+ number: string;
91
+ revision: number;
92
+ inputRevision: number;
93
+ status: TaskStatus;
94
+ createdBy: TaskActor;
95
+ owner: TaskActor;
96
+ createdAt: string;
97
+ updatedAt: string;
98
+ blocked?: {
99
+ reason: string;
100
+ message: string;
101
+ };
102
+ cancelRequested: boolean;
103
+ result?: TaskResult;
104
+ resultInputRevision?: number;
105
+ attempts: TaskAttempt[];
106
+ outcomes?: TaskOutcome[];
107
+ };
108
+ export type TaskMutation = {
109
+ requestId: string;
110
+ expectedRevision: number;
111
+ };
112
+ export type TaskInputWrite = Omit<TaskInput, "requiresReview"> & {
113
+ requiresReview?: boolean;
114
+ };
115
+ export type TaskCreate = {
116
+ requestId: string;
117
+ input: TaskInputWrite;
118
+ };
119
+ export type TaskEdit = TaskMutation & {
120
+ input: TaskInputWrite;
121
+ };
122
+ export type TaskAction = TaskMutation & {
123
+ action: "ready" | "draft" | "start" | "complete" | "cancel" | "accept" | "request_changes";
124
+ result?: TaskResult;
125
+ resultInputRevision?: number;
126
+ feedback?: string;
127
+ };
128
+ export type TaskPage = {
129
+ tasks: TaskRecord[];
130
+ nextCursor: string | null;
131
+ };
132
+ export type TaskCapabilities = {
133
+ version: 1;
134
+ prefix: string;
135
+ statuses: TaskStatus[];
136
+ maxPageSize: number;
137
+ };
138
+ export type TaskExecutorOption = {
139
+ id: string;
140
+ name: string;
141
+ capabilities: string[];
142
+ };
143
+ export type TaskExecutorOptions = {
144
+ options: TaskExecutorOption[];
145
+ defaultId: string | null;
146
+ };
147
+ export declare const taskStatuses: TaskStatus[];
148
+ export declare const taskExecutionTerminal: (state: TaskExecutionState) => boolean;
package/dist/tasks.js ADDED
@@ -0,0 +1,2 @@
1
+ export const taskStatuses = ["Draft", "Ready", "InProgress", "Blocked", "Done", "Cancelled"];
2
+ export const taskExecutionTerminal = (state) => ["completed", "failed", "cancelled", "interrupted"].includes(state);
@@ -1,4 +1,3 @@
1
- import type { SandboxIdentityContext } from "./sandbox-identity.js";
2
1
  import type { ProviderServerOptions } from "@hosty-sdk/app/providers/server";
3
2
  import { type ToolActivity, type PanelTarget, type UiBootstrap, type UiPanel, type UiResolvedPanel, type UiNavigationCatalog, type UiNavigationTarget, type UiResolvedNavigation } from "@hosty-sdk/app/ui/types";
4
3
  /** Server transport for generic UI metadata. Credentials never appear in returned metadata. */
@@ -22,7 +21,6 @@ type UiAuthenticatedContext = {
22
21
  status: "active";
23
22
  identity: {
24
23
  hostRole: string | null;
25
- sandbox?: SandboxIdentityContext;
26
24
  };
27
25
  } | {
28
26
  status: "not-present" | "expired" | "forbidden" | "unavailable" | "misconfigured";
package/dist/ui-server.js CHANGED
@@ -58,8 +58,6 @@ export async function createHostyUiResponse(request, context, options = {}) {
58
58
  }
59
59
  if (options.administratorOnly && context.session.identity.hostRole !== "host.admin")
60
60
  throw new UiError("admin_required", "This application requires administrator access.", 403);
61
- if (context.session.identity.sandbox)
62
- throw new UiError("unsupported_sandbox_ui", "Global application panels are unavailable inside this sandbox profile.", 409);
63
61
  const url = new URL(request.url);
64
62
  const prefix = (options.uiPath ?? "/api/hosty/ui").replace(/\/$/, "");
65
63
  const route = url.pathname.startsWith(prefix + "/") ? url.pathname.slice(prefix.length + 1) : "";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hosty-sdk/app",
3
- "version": "0.35.1",
3
+ "version": "0.37.0",
4
4
  "description": "Hosty app auth: session state machine, recovery bridge, Core revalidation, and the embedder responder.",
5
5
  "license": "AGPL-3.0-only",
6
6
  "type": "module",
@@ -132,6 +132,14 @@
132
132
  "./tool-activity/server": {
133
133
  "types": "./dist/tool-activity-server.d.ts",
134
134
  "default": "./dist/tool-activity-server.js"
135
+ },
136
+ "./tasks": {
137
+ "types": "./dist/tasks.d.ts",
138
+ "default": "./dist/tasks.js"
139
+ },
140
+ "./tasks/server": {
141
+ "types": "./dist/tasks-server.d.ts",
142
+ "default": "./dist/tasks-server.js"
135
143
  }
136
144
  },
137
145
  "scripts": {
@@ -1,10 +0,0 @@
1
- export interface SandboxTarget {
2
- runtimeInstanceId: string;
3
- generation: string;
4
- dataRevision: string;
5
- }
6
- export interface SandboxIdentityContext extends SandboxTarget {
7
- identityRealm: "sandbox" | "core";
8
- appRole: string;
9
- permissions: string[];
10
- }
@@ -1 +0,0 @@
1
- export {};