@relayfile/sdk 0.8.10 → 0.8.12

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/client.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type AdminIngressStatusResponse, type AdminSyncStatusResponse, type BulkWriteInput, type BulkWriteResponse, type BackendStatusResponse, type AckResponse, type CommitForkInput, type CommitForkResponse, type CreateForkInput, type DeleteFileInput, type DeadLetterItem, type DeadLetterFeedResponse, type DiscardForkInput, type EventFeedResponse, type ExportJsonResponse, type ExportOptions, type FileQueryResponse, type FileReadResponse, type FilesystemEvent, type GetEventsOptions, type GetAdminIngressStatusOptions, type GetAdminSyncStatusOptions, type GetOperationsOptions, type GetSyncDeadLettersOptions, type GetSyncIngressStatusOptions, type GetSyncStatusOptions, type ListTreeOptions, type OperationFeedResponse, type OperationStatusResponse, type QueuedResponse, type ResourceAtEventResult, type ReadFileInput, type QueryFilesOptions, type Subscription, type SyncIngressStatusResponse, type SyncStatusResponse, type TreeResponse, type WriteFileInput, type WriteQueuedResponse, type IngestWebhookInput, type WritebackItem, type AckWritebackInput, type AckWritebackResponse, type ChangeEvent, type ChangeLogQueryResult, type ChangeStreamConnection, type ChangeStreamConnectionOptions, type SubscribeOptions } from "./types.js";
1
+ import { type AdminIngressStatusResponse, type AdminSyncStatusResponse, type BulkWriteInput, type BulkWriteResponse, type BackendStatusResponse, type AckResponse, type CommitForkInput, type CommitForkResponse, type CreateForkInput, type DeleteFileInput, type DeadLetterItem, type DeadLetterFeedResponse, type DiscardForkInput, type EventFeedResponse, type ExportJsonResponse, type ExportOptions, type FileQueryResponse, type FileReadResponse, type FilesystemEvent, type GetEventsOptions, type GetAdminIngressStatusOptions, type GetAdminSyncStatusOptions, type GetOperationsOptions, type GetSyncDeadLettersOptions, type GetSyncIngressStatusOptions, type GetSyncStatusOptions, type ListTreeOptions, type OperationFeedResponse, type OperationStatusResponse, type QueuedResponse, type ResourceAtEventResult, type ReadFileInput, type QueryFilesOptions, type Subscription, type SyncIngressStatusResponse, type SyncStatusResponse, type TreeResponse, type WriteFileInput, type WriteQueuedResponse, type IngestWebhookInput, type WritebackItem, type AckWritebackInput, type AckWritebackResponse, type SweepWritebackDraftsInput, type SweepWritebackDraftsResponse, type ChangeEvent, type ChangeLogQueryResult, type ChangeStreamConnection, type ChangeStreamConnectionOptions, type SubscribeOptions } from "./types.js";
2
2
  import type { ForkHandle } from "@relayfile/core";
3
3
  /**
4
4
  * Bearer token or token factory used for Relayfile API requests.
@@ -127,6 +127,11 @@ export declare class RelayFileClient {
127
127
  ingestWebhook(input: IngestWebhookInput): Promise<QueuedResponse>;
128
128
  listPendingWritebacks(workspaceId: string, correlationId?: string, signal?: AbortSignal): Promise<WritebackItem[]>;
129
129
  ackWriteback(input: AckWritebackInput): Promise<AckWritebackResponse>;
130
+ /**
131
+ * One-time residue sweep for accumulated writeback drafts (issue #242).
132
+ * Dry run unless `apply` is true; classification-exempt service-side.
133
+ */
134
+ sweepWritebackDrafts(input: SweepWritebackDraftsInput): Promise<SweepWritebackDraftsResponse>;
130
135
  private cacheWireChangeEvent;
131
136
  private primeReplayCache;
132
137
  private resolveWorkspaceId;
package/dist/client.js CHANGED
@@ -1523,7 +1523,26 @@ export class RelayFileClient {
1523
1523
  correlationId: input.correlationId,
1524
1524
  body: {
1525
1525
  success: input.success,
1526
- error: input.error
1526
+ error: input.error,
1527
+ externalId: input.externalId,
1528
+ canonicalPath: input.canonicalPath
1529
+ },
1530
+ signal: input.signal
1531
+ });
1532
+ }
1533
+ /**
1534
+ * One-time residue sweep for accumulated writeback drafts (issue #242).
1535
+ * Dry run unless `apply` is true; classification-exempt service-side.
1536
+ */
1537
+ async sweepWritebackDrafts(input) {
1538
+ return this.request({
1539
+ method: "POST",
1540
+ path: `/v1/workspaces/${encodeURIComponent(input.workspaceId)}/writeback/sweep-drafts`,
1541
+ correlationId: input.correlationId,
1542
+ body: {
1543
+ pathPrefix: input.pathPrefix,
1544
+ patterns: input.patterns,
1545
+ apply: input.apply === true
1527
1546
  },
1528
1547
  signal: input.signal
1529
1548
  });
package/dist/index.d.ts CHANGED
@@ -2,14 +2,14 @@ export { RelayFileClient, DEFAULT_RELAYFILE_BASE_URL, type AccessTokenProvider,
2
2
  export { RelayfileSetup, RELAYFILE_SDK_VERSION, WorkspaceHandle } from "./setup.js";
3
3
  export { type RelayfileCloudLoginOptions, type RelayfileCloudTokenSet, type RelayfileCloudTokenSetupOptions } from "./cloud-login.js";
4
4
  export { CloudAbortError, CloudApiError, CloudTimeoutError, InvalidLocalDirError, InvalidMountModeError, InvalidRemotePathError, IntegrationConnectionTimeoutError, MalformedCloudResponseError, MissingConnectionIdError, MountModeUnavailableError, MountReadyTimeoutError, MountSessionInputError, ProviderNotConnectedError, ProviderNotReadyError, RelayfileSetupError, UnknownProviderError } from "./setup-errors.js";
5
- export { type EnsureMountedWorkspaceInput, WORKSPACE_INTEGRATION_PROVIDERS, type AgentWorkspaceInvite, type AgentWorkspaceInviteOptions, type AgentWorkspaceScopedInviteOptions, type ConnectIntegrationOptions, type ConnectIntegrationResult, type CreateWorkspaceOptions, type JoinWorkspaceOptions, type MountLauncher, type MountLauncherEvent, type MountLauncherInstance, type MountLauncherStart, type MountMode, type MountSessionRequest, type MountSessionResponse, type MountSessionResult, type MountedWorkspaceHandle, type MountedWorkspaceStatus, type MountWorkspaceInput, type ReadMountedWorkspaceStatusInput, type RelayfileSetupOptions, type RelayfileSetupRetryOptions, type WaitForConnectionOptions, type WorkspaceInfo, type WorkspaceIntegrationProvider, type WorkspaceMountEnv, type WorkspaceMountEnvOptions, type WorkspacePermissions } from "./setup-types.js";
5
+ export { type EnsureMountedWorkspaceInput, WORKSPACE_INTEGRATION_PROVIDERS, type AgentWorkspaceInvite, type AgentWorkspaceInviteOptions, type AgentWorkspaceScopedInviteOptions, type ConnectIntegrationOptions, type ConnectIntegrationResult, type CreateWorkspaceOptions, type JoinWorkspaceOptions, type MountLauncher, type MountLauncherEvent, type MountLauncherInstance, type MountLauncherStart, type MountLocalLayout, type MountMode, type MountSessionRequest, type MountSessionResponse, type MountSessionResult, type MountSyncMode, type MountedWorkspaceHandle, type MountedWorkspaceStatus, type MountWorkspaceInput, type ReadMountedWorkspaceStatusInput, type RelayfileSetupOptions, type RelayfileSetupRetryOptions, type WaitForConnectionOptions, type WorkspaceInfo, type WorkspaceIntegrationProvider, type WorkspaceMountEnv, type WorkspaceMountEnvOptions, type WorkspacePermissions } from "./setup-types.js";
6
6
  export { RelayFileSync, type RelayFileSyncOptions, type RelayFileSyncPong, type RelayFileSyncReconnectOptions, type RelayFileSyncSocket, type RelayFileSyncStart, type RelayFileSyncState, type RelayFileSyncTokenProvider } from "./sync.js";
7
7
  export { onWrite, pathMatches, type OnWriteClient, type OnWriteHandler, type OnWriteHandlerError, type OnWriteOptions } from "./onWrite.js";
8
8
  export { InvalidStateError, PayloadTooLargeError, QueueFullError, RelayFileApiError, RevisionConflictError } from "./errors.js";
9
9
  export { IntegrationProvider, computeCanonicalPath } from "./provider.js";
10
10
  export type { WebhookInput, ListProviderFilesOptions, WatchProviderEventsOptions } from "./provider.js";
11
11
  export type { ConnectionProvider, NormalizedWebhook, ProxyHeaders, ProxyMethod, ProxyQuery, ProxyRequest, ProxyResponse, } from "./connection.js";
12
- export type { AckResponse, AckWritebackInput, AckWritebackResponse, AdminIngressAlert, AdminIngressAlertProfile, AdminIngressEffectiveAlertProfile, AdminIngressAlertSeverity, AdminIngressAlertThresholds, AdminIngressAlertTotals, AdminIngressAlertType, AdminIngressStatusResponse, AdminSyncAlert, AdminSyncAlertSeverity, AdminSyncAlertThresholds, AdminSyncAlertTotals, AdminSyncAlertType, AdminSyncStatusResponse, BackendStatusResponse, BulkWriteFile, BulkWriteInput, BulkWriteResponse, ChangeLogQueryResult, ChangeEvent, ChangeEventActor, ChangeEventResource, ChangeEventSummary, ChangeStreamConnection, ChangeStreamConnectionOptions, CommitForkInput, CommitForkResponse, ConflictErrorResponse, CreateForkInput, ContentIdentity, DeleteFileInput, DeadLetterFeedResponse, DeadLetterItem, DigestBullet, DigestContext, DigestHandler, DigestSection, DigestWindow, DiscardForkInput, ErrorResponse, EventSummary, EventFeedResponse, ExportFormat, ExportJsonResponse, ExportOptions, FileQueryItem, FileQueryResponse, FileReadResponse, FileSemantics, FileWriteRequest, FilesystemEvent, FilesystemEventType, EventOrigin, Expansion, ExpansionLevel, GetEventsOptions, GetAdminSyncStatusOptions, GetAdminIngressStatusOptions, GetOperationsOptions, GetSyncDeadLettersOptions, GetSyncIngressStatusOptions, GetSyncStatusOptions, IngestWebhookInput, LayoutManifest, LayoutManifestAlias, LayoutManifestResource, ListTreeOptions, OperationFeedResponse, OperationStatus, OperationStatusResponse, QueuedResponse, QueryFilesOptions, ReadFileInput, ReplayOptions, ResourceAtEventResult, SummaryExpansion, FullExpansion, DiffExpansion, ThreadExpansion, RelayFileJwtClaims, SubscribeOptions, Subscription, SyncIngressStatusResponse, SyncProviderStatus, SyncProviderStatusState, SyncRefreshRequest, SyncStatusResponse, TreeEntry, TreeResponse, WritebackActionType, WritebackDeadLetterError, WritebackDeadLetterErrorCode, WritebackListState, WritebackState, WritebackItem, WritebackItemDetail, WritebackSchemaRef, WriteFileInput, WriteQueuedResponse } from "./types.js";
12
+ export type { AckResponse, AckWritebackInput, AckWritebackDraftDisposition, AckWritebackResponse, AdminIngressAlert, AdminIngressAlertProfile, AdminIngressEffectiveAlertProfile, AdminIngressAlertSeverity, AdminIngressAlertThresholds, AdminIngressAlertTotals, AdminIngressAlertType, AdminIngressStatusResponse, AdminSyncAlert, AdminSyncAlertSeverity, AdminSyncAlertThresholds, AdminSyncAlertTotals, AdminSyncAlertType, AdminSyncStatusResponse, BackendStatusResponse, BulkWriteFile, BulkWriteInput, BulkWriteResponse, ChangeLogQueryResult, ChangeEvent, ChangeEventActor, ChangeEventResource, ChangeEventSummary, ChangeStreamConnection, ChangeStreamConnectionOptions, CommitForkInput, CommitForkResponse, ConflictErrorResponse, CreateForkInput, ContentIdentity, DeleteFileInput, DeadLetterFeedResponse, DeadLetterItem, DigestBullet, DigestContext, DigestHandler, DigestSection, DigestWindow, DiscardForkInput, ErrorResponse, EventSummary, EventFeedResponse, ExportFormat, ExportJsonResponse, ExportOptions, FileQueryItem, FileQueryResponse, FileReadResponse, FileSemantics, FileWriteRequest, FilesystemEvent, FilesystemEventType, EventOrigin, Expansion, ExpansionLevel, GetEventsOptions, GetAdminSyncStatusOptions, GetAdminIngressStatusOptions, GetOperationsOptions, GetSyncDeadLettersOptions, GetSyncIngressStatusOptions, GetSyncStatusOptions, IngestWebhookInput, LayoutManifest, LayoutManifestAlias, LayoutManifestResource, ListTreeOptions, OperationFeedResponse, OperationStatus, OperationStatusResponse, QueuedResponse, QueryFilesOptions, ReadFileInput, ReplayOptions, ResourceAtEventResult, SummaryExpansion, FullExpansion, DiffExpansion, ThreadExpansion, RelayFileJwtClaims, SubscribeOptions, Subscription, SyncIngressStatusResponse, SyncProviderStatus, SyncProviderStatusState, SyncRefreshRequest, SyncStatusResponse, SweepWritebackDraftsInput, SweepWritebackDraftsResponse, TreeEntry, TreeResponse, WritebackActionType, WritebackDeadLetterError, WritebackDeadLetterErrorCode, WritebackListState, WritebackState, WritebackItem, WritebackItemDetail, WritebackSchemaRef, WriteFileInput, WriteQueuedResponse } from "./types.js";
13
13
  export type { ForkHandle, ForkOptions } from "@relayfile/core";
14
14
  export type { WriteEvent, WriteEventActor, WriteEventOperation, WriteEventSource } from "@relayfile/core";
15
15
  export { WritebackConsumer } from "./writeback-consumer.js";
@@ -20,7 +20,7 @@ export function createDefaultMountLauncher(options = {}) {
20
20
  };
21
21
  }
22
22
  export async function readMountedWorkspaceStatus(input) {
23
- const state = await readMountStateFile(input.localDir);
23
+ const state = await readMountStateFile(resolveMountLocalDir(input.localDir, input.remotePath, input.localLayout));
24
24
  if (state && !isMountStateStale(state)) {
25
25
  return {
26
26
  ready: isMountStateReady(state),
@@ -46,7 +46,8 @@ export async function readMountedWorkspaceStatus(input) {
46
46
  }
47
47
  async function startRelayfileMount(input, options) {
48
48
  const localDir = path.resolve(input.env.RELAYFILE_LOCAL_DIR ?? process.cwd());
49
- const relayDir = path.join(localDir, ".relay");
49
+ const mountLocalDir = resolveMountLocalDir(localDir, input.env.RELAYFILE_REMOTE_PATH, input.env.RELAYFILE_MOUNT_LOCAL_LAYOUT);
50
+ const relayDir = path.join(mountLocalDir, ".relay");
50
51
  const logPath = path.join(relayDir, "mount.log");
51
52
  const pidPath = path.join(relayDir, "mount.pid");
52
53
  await mkdir(relayDir, { recursive: true });
@@ -54,7 +55,7 @@ async function startRelayfileMount(input, options) {
54
55
  const command = await resolveRelayfileMountCommand();
55
56
  const args = input.background === false ? ["--once"] : [];
56
57
  const child = (options.spawnImpl ?? spawn)(command, args, {
57
- cwd: input.cwd ?? localDir,
58
+ cwd: input.cwd ?? mountLocalDir,
58
59
  env: {
59
60
  ...process.env,
60
61
  ...input.env
@@ -73,7 +74,7 @@ async function startRelayfileMount(input, options) {
73
74
  pidPath,
74
75
  outputBuffer,
75
76
  input,
76
- localDir,
77
+ localDir: mountLocalDir,
77
78
  now: options.now ?? Date.now,
78
79
  readyPollIntervalMs: options.readyPollIntervalMs ?? DEFAULT_READY_POLL_INTERVAL_MS
79
80
  });
@@ -113,6 +114,8 @@ class RelayfileMountProcessInstance {
113
114
  workspaceId: this.input.env.RELAYFILE_WORKSPACE ?? "",
114
115
  remotePath: this.input.env.RELAYFILE_REMOTE_PATH ?? "/",
115
116
  mode: normalizeMountMode(this.input.env.RELAYFILE_MOUNT_MODE) ?? "poll",
117
+ localLayout: normalizeMountLocalLayout(this.input.env.RELAYFILE_MOUNT_LOCAL_LAYOUT),
118
+ syncMode: normalizeMountSyncMode(this.input.env.RELAYFILE_MOUNT_SYNC_MODE),
116
119
  relayfileBaseUrl: this.input.env.RELAYFILE_BASE_URL ?? "",
117
120
  relayfileToken: this.input.env.RELAYFILE_TOKEN ?? "",
118
121
  expiresAt: null,
@@ -251,6 +254,32 @@ function isMountStateStale(state) {
251
254
  function normalizeMountMode(mode) {
252
255
  return mode === "fuse" ? "fuse" : mode === "poll" ? "poll" : undefined;
253
256
  }
257
+ function normalizeMountLocalLayout(layout) {
258
+ return layout === "scoped" ? "scoped" : "exact";
259
+ }
260
+ function normalizeMountSyncMode(mode) {
261
+ return mode === "write-only" ? "write-only" : "mirror";
262
+ }
263
+ function resolveMountLocalDir(localDir, remotePath, localLayout) {
264
+ const root = path.resolve(localDir);
265
+ if (normalizeMountLocalLayout(localLayout) !== "scoped") {
266
+ return root;
267
+ }
268
+ const normalizedRemote = normalizeRemotePath(remotePath);
269
+ if (normalizedRemote === "/") {
270
+ return root;
271
+ }
272
+ return path.join(root, ...normalizedRemote.split("/").filter(Boolean));
273
+ }
274
+ function normalizeRemotePath(remotePath) {
275
+ const trimmed = typeof remotePath === "string" ? remotePath.trim() : "";
276
+ if (!trimmed || trimmed === "/") {
277
+ return "/";
278
+ }
279
+ const slashNormalized = trimmed.replace(/\\/g, "/");
280
+ const normalized = path.posix.normalize(slashNormalized.startsWith("/") ? slashNormalized : `/${slashNormalized}`);
281
+ return normalized === "/" ? "/" : normalized.replace(/\/+$/, "");
282
+ }
254
283
  function normalizeIsoString(value) {
255
284
  if (typeof value !== "string" || value.trim() === "") {
256
285
  return undefined;
@@ -67,6 +67,8 @@ export interface WorkspaceMountEnvOptions {
67
67
  }
68
68
  export type WorkspaceMountEnv = Record<string, string>;
69
69
  export type MountMode = "poll" | "fuse";
70
+ export type MountLocalLayout = "exact" | "scoped";
71
+ export type MountSyncMode = "mirror" | "write-only";
70
72
  export interface MountSessionRequest {
71
73
  localDir: string;
72
74
  remotePath?: string;
@@ -98,6 +100,8 @@ export interface MountSessionResult {
98
100
  remotePath: string;
99
101
  localDir: string;
100
102
  mode: MountMode;
103
+ localLayout: MountLocalLayout;
104
+ syncMode: MountSyncMode;
101
105
  scopes: string[];
102
106
  tokenIssuedAt: string | null;
103
107
  expiresAt: string | null;
@@ -122,6 +126,8 @@ export interface ReadMountedWorkspaceStatusInput {
122
126
  workspaceId: string;
123
127
  remotePath: string;
124
128
  mode: MountMode;
129
+ localLayout?: MountLocalLayout;
130
+ syncMode?: MountSyncMode;
125
131
  relayfileBaseUrl: string;
126
132
  relayfileToken: string;
127
133
  expiresAt: string | null;
@@ -167,6 +173,8 @@ export interface MountWorkspaceInput {
167
173
  localDir: string;
168
174
  remotePath?: string;
169
175
  mode?: MountMode;
176
+ localLayout?: MountLocalLayout;
177
+ syncMode?: MountSyncMode;
170
178
  background?: boolean;
171
179
  agentName?: string;
172
180
  scopes?: string[];
package/dist/setup.js CHANGED
@@ -16,6 +16,8 @@ const DEFAULT_WAIT_INTERVAL_MS = 2_000;
16
16
  const DEFAULT_WAIT_TIMEOUT_MS = 300_000;
17
17
  const DEFAULT_MOUNT_READY_TIMEOUT_MS = 60_000;
18
18
  const DEFAULT_MOUNT_AGENT_NAME = "relayfile-mount";
19
+ const DEFAULT_MOUNT_LOCAL_LAYOUT = "exact";
20
+ const DEFAULT_MOUNT_SYNC_MODE = "mirror";
19
21
  const TOKEN_REFRESH_AGE_MS = 55 * 60 * 1000;
20
22
  const nodeOnlyMountLauncher = {
21
23
  async start() {
@@ -142,6 +144,8 @@ export class RelayfileSetup {
142
144
  localDir: normalized.localDir,
143
145
  remotePath: normalized.remotePath,
144
146
  mode: normalized.mode,
147
+ localLayout: normalized.localLayout,
148
+ syncMode: normalized.syncMode,
145
149
  background: normalized.background,
146
150
  agentName: normalized.agentName,
147
151
  scopes: normalized.scopes,
@@ -246,13 +250,18 @@ export class RelayfileSetup {
246
250
  : undefined
247
251
  });
248
252
  try {
249
- return validateMountSessionResponse(await workspace.requestJson({
253
+ const session = validateMountSessionResponse(await workspace.requestJson({
250
254
  operation: "mountWorkspace",
251
255
  method: "POST",
252
256
  path: `api/v1/workspaces/${encodeURIComponent(workspace.workspaceId)}/relayfile/mount-session`,
253
257
  body: request,
254
258
  signal: input.signal
255
259
  }), input.localDir);
260
+ return {
261
+ ...session,
262
+ localLayout: input.localLayout,
263
+ syncMode: input.syncMode
264
+ };
256
265
  }
257
266
  catch (error) {
258
267
  throw mapMountSessionError(error, request);
@@ -731,6 +740,8 @@ class MountedWorkspaceHandleImpl {
731
740
  workspaceId: this.workspaceId,
732
741
  remotePath: this.remotePath,
733
742
  mode: this.mode,
743
+ localLayout: this.mountSession.localLayout,
744
+ syncMode: this.mountSession.syncMode,
734
745
  relayfileBaseUrl: this.mountSession.relayfileBaseUrl,
735
746
  relayfileToken: this.mountSession.relayfileToken,
736
747
  expiresAt: this.expiresAt,
@@ -837,6 +848,8 @@ function validateMountSessionResponse(payload, localDir) {
837
848
  remotePath: requireStringField(payload, "remotePath"),
838
849
  localDir,
839
850
  mode: requireMountModeField(payload, "mode"),
851
+ localLayout: DEFAULT_MOUNT_LOCAL_LAYOUT,
852
+ syncMode: DEFAULT_MOUNT_SYNC_MODE,
840
853
  scopes: requireStringArrayField(payload, "scopes"),
841
854
  tokenIssuedAt: readNullableStringField(payload, "tokenIssuedAt"),
842
855
  expiresAt: readNullableStringField(payload, "expiresAt"),
@@ -930,6 +943,8 @@ function normalizeMountWorkspaceInput(input) {
930
943
  localDir: resolveLocalDir(localDir),
931
944
  remotePath: normalizeMountRemotePath(input.remotePath),
932
945
  mode: normalizeMountModeInput(input.mode),
946
+ localLayout: normalizeMountLocalLayoutInput(input.localLayout),
947
+ syncMode: normalizeMountSyncModeInput(input.syncMode),
933
948
  background: input.background !== false,
934
949
  agentName: normalizeNonEmptyString(input.agentName),
935
950
  scopes: input.scopes && input.scopes.length > 0 ? [...input.scopes] : undefined,
@@ -954,6 +969,20 @@ function normalizeMountModeInput(mode) {
954
969
  }
955
970
  return normalized;
956
971
  }
972
+ function normalizeMountLocalLayoutInput(layout) {
973
+ const normalized = normalizeNonEmptyString(layout) ?? DEFAULT_MOUNT_LOCAL_LAYOUT;
974
+ if (normalized !== "exact" && normalized !== "scoped") {
975
+ throw new MountSessionInputError(`Invalid localLayout "${normalized}" for mount session.`);
976
+ }
977
+ return normalized;
978
+ }
979
+ function normalizeMountSyncModeInput(mode) {
980
+ const normalized = normalizeNonEmptyString(mode) ?? DEFAULT_MOUNT_SYNC_MODE;
981
+ if (normalized !== "mirror" && normalized !== "write-only") {
982
+ throw new MountSessionInputError(`Invalid syncMode "${normalized}" for mount session.`);
983
+ }
984
+ return normalized;
985
+ }
957
986
  function normalizeMountRemotePath(remotePath) {
958
987
  const normalized = normalizeNonEmptyString(remotePath) ?? "/";
959
988
  if (normalized.includes("\u0000")) {
@@ -1052,6 +1081,8 @@ function buildMountedWorkspaceEnv(mountSession) {
1052
1081
  RELAYFILE_REMOTE_PATH: mountSession.remotePath,
1053
1082
  RELAYFILE_LOCAL_DIR: mountSession.localDir,
1054
1083
  RELAYFILE_MOUNT_MODE: mountSession.mode,
1084
+ RELAYFILE_MOUNT_LOCAL_LAYOUT: mountSession.localLayout,
1085
+ RELAYFILE_MOUNT_SYNC_MODE: mountSession.syncMode,
1055
1086
  RELAYCAST_API_KEY: mountSession.relaycastApiKey,
1056
1087
  RELAY_API_KEY: mountSession.relaycastApiKey,
1057
1088
  RELAYCAST_BASE_URL: relaycastBaseUrl,
package/dist/types.d.ts CHANGED
@@ -700,12 +700,58 @@ export interface AckWritebackInput {
700
700
  itemId: string;
701
701
  success: boolean;
702
702
  error?: string;
703
+ /**
704
+ * Provider-assigned id of the created/updated object (e.g. the Slack
705
+ * message ts). When present on a successful ack, the service reconciles
706
+ * the agent-authored draft file per the draftFile() rename contract
707
+ * (issue #242): the draft is renamed to the canonical id, or removed when
708
+ * the canonical record already materialized. The mutation is
709
+ * classification-exempt — it can never enqueue a new writeback.
710
+ */
711
+ externalId?: string;
712
+ /**
713
+ * Optional canonical projection path for the draft rename. Must stay under
714
+ * the same provider root as the draft; otherwise the service falls back to
715
+ * the externalId-derived name next to the draft.
716
+ */
717
+ canonicalPath?: string;
703
718
  correlationId?: string;
704
719
  signal?: AbortSignal;
705
720
  }
721
+ /** Disposition of the agent-authored draft file after a successful ack. */
722
+ export interface AckWritebackDraftDisposition {
723
+ action: "renamed" | "removed" | "none";
724
+ from?: string;
725
+ to?: string;
726
+ }
706
727
  export interface AckWritebackResponse {
707
728
  status: "acknowledged";
708
729
  id: string;
709
730
  correlationId?: string;
710
731
  success: boolean;
732
+ /** Present only when the ack was successful and carried an externalId. */
733
+ draft?: AckWritebackDraftDisposition;
734
+ }
735
+ export interface SweepWritebackDraftsInput {
736
+ workspaceId: string;
737
+ /** Restrict the sweep to a subtree. */
738
+ pathPrefix?: string;
739
+ /** Basename globs for hand-named drafts, e.g. "wb-*.json". */
740
+ patterns?: string[];
741
+ /** Execute removals; when false the sweep is a dry run. */
742
+ apply?: boolean;
743
+ correlationId?: string;
744
+ signal?: AbortSignal;
745
+ }
746
+ export interface SweepWritebackDraftsResponse {
747
+ dryRun: boolean;
748
+ scanned: number;
749
+ removed: Array<{
750
+ path: string;
751
+ reason: "space-uuid-draft" | "pattern";
752
+ }>;
753
+ skipped: Array<{
754
+ path: string;
755
+ reason: "pending-writeback" | "provider-linked";
756
+ }>;
711
757
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@relayfile/sdk",
3
- "version": "0.8.10",
3
+ "version": "0.8.12",
4
4
  "description": "TypeScript SDK for relayfile — real-time filesystem for humans and agents",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -55,7 +55,7 @@
55
55
  "prepublishOnly": "npm run build"
56
56
  },
57
57
  "dependencies": {
58
- "@relayfile/core": "0.8.10",
58
+ "@relayfile/core": "0.8.12",
59
59
  "ignore": "^7.0.5",
60
60
  "tar": "^7.5.10"
61
61
  },