@pstdio/sdk 0.25.0 → 0.35.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
@@ -63,3 +63,32 @@ The host delivers only events matching the view's project. Projectless events re
63
63
  Development and installed consumers both load built SDK entries. Repository development builds this package before starting the source CLI. Builds stage release files under `.publish` through the shared release script. Package verification installs those same staged artifacts into a temporary directory outside the monorepo and checks every entry point.
64
64
 
65
65
  Host authors should use the [workbench guide](https://github.com/pufflyai/prompt-studio/blob/main/packages/pstdio-workbench/README.md). Extension authors should use this SDK and public UI packages.
66
+
67
+ ## Workspace contract release bridge
68
+
69
+ The alpha.10 host now provides workspace APIs alongside its existing repository APIs.
70
+ Use `ctx.projectFiles` for the project's default workspace and `ctx.workspaceFiles`
71
+ for the invocation's working files. Project file operations check the current workspace
72
+ readiness and file capabilities. Remote workspaces never fall back to local files.
73
+
74
+ `ctx.workspaces.getDefault()` returns the project workspace.
75
+ `ctx.workspaces.listProviders()` returns available providers, their optional `icon`, and their parameters.
76
+ Render these declared parameters after the user selects a workspace type. The Git
77
+ provider supplies a **Base branch** selection; cloud providers supply their own fields.
78
+ Local setup failures reject creation with the saved workspace ID and setup error.
79
+ The workspace remains available for diagnosis and retry.
80
+ Workspace context records expose `root_path` for a local directory and retain
81
+ `worktree_path` during this release bridge.
82
+
83
+ The SDK client exposes `client.workspaces.listProviders(projectId)` and
84
+ `client.filesystem.createDirectory({ parent_path, name })`.
85
+ Directory creation accepts one child name under an existing parent.
86
+
87
+ Webviews can set `workspaceId` in `createWebviewClient(host, { workspaceId })`
88
+ to run commands in that workspace. The host validates project ownership.
89
+
90
+ Extensions that work with both host contracts can declare the exact versions
91
+ `1.0.0-alpha.10 || 1.0.0-alpha.11` in `engines.pstdio`.
92
+ General version ranges and wildcards remain unsupported.
93
+ This declaration requires the bridge host; older alpha.10 hosts accept only one version.
94
+ See [the staged release ADR](../../.pstdio/docs/adrs/0030-temporary-workspace-contract-release-bridge.md).
package/dist/api/index.js CHANGED
@@ -6308,6 +6308,10 @@ var automationRunEventSchema = object({
6308
6308
  createdAt: string2()
6309
6309
  });
6310
6310
  var listAutomationRunEventsResponseSchema = object({ events: array(automationRunEventSchema) });
6311
+ // ../pstdio-api-contracts/src/extension-kernel/api-versions.ts
6312
+ var numericIdentifier = "(?:0|[1-9]\\d*)";
6313
+ var prereleaseIdentifier = `(?:${numericIdentifier}|\\d*[A-Za-z-][0-9A-Za-z-]*)`;
6314
+ var exactVersion = new RegExp(`^${numericIdentifier}\\.${numericIdentifier}\\.${numericIdentifier}(?:-${prereleaseIdentifier}(?:\\.${prereleaseIdentifier})*)?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?$`);
6311
6315
  // ../pstdio-api-contracts/src/extension-kernel/builtin-refs.ts
6312
6316
  var hostRef = (kind, id) => ({ extensionId: "pstdio", kind, id });
6313
6317
  var workbenchModes = {
@@ -6439,6 +6443,11 @@ var workspaceSlots = {
6439
6443
  var projectEvents = {
6440
6444
  opened: hostEventRef("project.opened")
6441
6445
  };
6446
+ var viewDataEvents = {
6447
+ sessionsChanged: hostEventRef("view.sessions.changed"),
6448
+ workspacesChanged: hostEventRef("view.workspaces.changed"),
6449
+ repositoriesChanged: hostEventRef("view.repositories.changed")
6450
+ };
6442
6451
  var sessionEvents = {
6443
6452
  started: hostEventRef("session.started"),
6444
6453
  resumed: hostEventRef("session.resumed"),
@@ -6598,6 +6607,179 @@ var fileRecordSchema = object({
6598
6607
  created_at: string2(),
6599
6608
  updated_at: string2()
6600
6609
  });
6610
+ // ../pstdio-api-contracts/src/history-turn-evidence.ts
6611
+ var historyTurnEvidence = (known, native, prompt, body) => {
6612
+ const signatures = (turn) => JSON.stringify([prompt(turn), body(turn)]);
6613
+ if (known.length === native.length && known.every((turn, index) => signatures(turn) === signatures(native[index]))) {
6614
+ return signatures;
6615
+ }
6616
+ const tokens = (turns) => {
6617
+ const positions = new Map;
6618
+ turns.forEach((turn, index) => {
6619
+ for (const value of new Set(body(turn))) {
6620
+ const token = JSON.stringify([prompt(turn), value]);
6621
+ positions.set(token, positions.has(token) ? -1 : index);
6622
+ }
6623
+ });
6624
+ return positions;
6625
+ };
6626
+ const left = tokens(known);
6627
+ const right = tokens(native);
6628
+ const matches = new Map;
6629
+ const reverse = new Map;
6630
+ for (const [token, index] of left) {
6631
+ const other = right.get(token);
6632
+ if (index < 0 || other === undefined || other < 0)
6633
+ continue;
6634
+ const targets = matches.get(index) ?? new Set;
6635
+ targets.add(other);
6636
+ matches.set(index, targets);
6637
+ const sources = reverse.get(other) ?? new Set;
6638
+ sources.add(index);
6639
+ reverse.set(other, sources);
6640
+ }
6641
+ const keys = new Map;
6642
+ known.forEach((turn, index) => {
6643
+ keys.set(turn, `known:${index}`);
6644
+ });
6645
+ native.forEach((turn, index) => {
6646
+ keys.set(turn, `native:${index}`);
6647
+ });
6648
+ for (const [index, targets] of matches) {
6649
+ if (targets.size !== 1)
6650
+ continue;
6651
+ const other = [...targets][0];
6652
+ if (reverse.get(other)?.size !== 1)
6653
+ continue;
6654
+ const key = `pair:${index}:${other}`;
6655
+ keys.set(known[index], key);
6656
+ keys.set(native[other], key);
6657
+ }
6658
+ return (turn) => keys.get(turn);
6659
+ };
6660
+
6661
+ // ../pstdio-api-contracts/src/history-reconciliation.ts
6662
+ class HistoryConflict extends Error {
6663
+ }
6664
+ var submittedPrompt = (message) => message.parts.filter((part) => part.type === "text").map((part) => part.text).join(`
6665
+ `).replace(/\n\n<session-attachments>[\s\S]*<\/session-attachments>\s*$/, "").trim();
6666
+ var historyValueKey = (value) => JSON.stringify(value, (_key, item) => {
6667
+ if (!item || typeof item !== "object" || Array.isArray(item))
6668
+ return item;
6669
+ return Object.fromEntries(Object.entries(item).sort(([a], [b]) => a.localeCompare(b)));
6670
+ });
6671
+ var historyMessageKey = (message) => historyValueKey([message.role, message.parts]);
6672
+ var positions = (items, key) => {
6673
+ const result = new Map;
6674
+ items.forEach((item, index) => {
6675
+ const value = key(item);
6676
+ const indices = result.get(value) ?? [];
6677
+ indices.push(index);
6678
+ result.set(value, indices);
6679
+ });
6680
+ return result;
6681
+ };
6682
+ var mergeOrderedHistory = (known, native, key, merge, refineKey) => {
6683
+ if (!known.length)
6684
+ return native;
6685
+ if (!native.length)
6686
+ return known;
6687
+ const knownPositions = positions(known, key);
6688
+ const nativePositions = positions(native, key);
6689
+ const anchors = [];
6690
+ for (const [value, indices] of knownPositions) {
6691
+ const other = nativePositions.get(value);
6692
+ if (indices.length === 1 && other?.length === 1)
6693
+ anchors.push([indices[0], other[0]]);
6694
+ }
6695
+ if (!anchors.length) {
6696
+ if (refineKey) {
6697
+ const refined = mergeOrderedHistory(known, native, refineKey(known, native), merge);
6698
+ return refined;
6699
+ }
6700
+ if (known.length === native.length && known.every((item, index) => key(item) === key(native[index]))) {
6701
+ return known.map((item, index) => merge(item, native[index]));
6702
+ }
6703
+ throw new HistoryConflict("ambiguous_interval");
6704
+ }
6705
+ let left = 0;
6706
+ let right = 0;
6707
+ const result = [];
6708
+ for (const [a, b] of anchors) {
6709
+ if (b < right)
6710
+ throw new HistoryConflict("conflicting_order");
6711
+ result.push(...mergeOrderedHistory(known.slice(left, a), native.slice(right, b), key, merge, refineKey));
6712
+ result.push(merge(known[a], native[b]));
6713
+ left = a + 1;
6714
+ right = b + 1;
6715
+ }
6716
+ result.push(...mergeOrderedHistory(known.slice(left), native.slice(right), key, merge, refineKey));
6717
+ return result;
6718
+ };
6719
+ var splitHistoryTurns = (messages) => {
6720
+ const turns = [];
6721
+ for (const message of messages) {
6722
+ if (message.role === "user" || !turns.length)
6723
+ turns.push([]);
6724
+ turns[turns.length - 1].push(message);
6725
+ }
6726
+ return turns;
6727
+ };
6728
+ var mergeHistoryMetadata = (known, native) => {
6729
+ const files = native.parts.filter((part) => part.type === "file");
6730
+ const fileKeys = new Set(files.map((part) => part.fileId ?? part.url));
6731
+ const missing = known.parts.filter((part) => part.type === "file" && !fileKeys.has(part.fileId ?? part.url));
6732
+ const parts = native.role === "user" ? [...known.parts.filter((part) => part.type === "text"), ...native.parts.filter((part) => part.type !== "text")] : native.parts;
6733
+ return { ...known, ...native, parts: [...parts, ...missing] };
6734
+ };
6735
+ var isUsage = (message) => message.role === "system" && message.parts.length > 0 && message.parts.every((part) => part.type === "token_usage");
6736
+ var combineTextRuns = (messages) => {
6737
+ const runs = [];
6738
+ for (const message of messages) {
6739
+ const previous = runs.at(-1);
6740
+ const part = message.parts[0];
6741
+ const previousPart = previous?.parts[0];
6742
+ if (previous && message.role === previous.role && message.parts.length === 1 && previous.parts.length === 1 && (part.type === "text" || part.type === "reasoning") && previousPart?.type === part.type) {
6743
+ runs[runs.length - 1] = { ...previous, parts: [{ ...part, text: previousPart.text + part.text }] };
6744
+ } else
6745
+ runs.push(message);
6746
+ }
6747
+ return runs;
6748
+ };
6749
+ var reconcileMessageHistory = (input, projection = {}) => {
6750
+ const key = projection.key ?? historyMessageKey;
6751
+ const merge = projection.merge ?? mergeHistoryMetadata;
6752
+ const generated = projection.isGenerated ?? isUsage;
6753
+ const mergeTurn = (known, native) => {
6754
+ const startsWithUser = known[0]?.role === "user" && native[0]?.role === "user";
6755
+ const body = (turn) => combineTextRuns(turn.slice(startsWithUser ? 1 : 0).filter((message) => !generated(message)));
6756
+ const messages = mergeOrderedHistory(body(known), body(native), key, merge);
6757
+ const metadata = [...native.filter(generated)];
6758
+ const counts = new Map;
6759
+ metadata.forEach((message) => {
6760
+ counts.set(historyMessageKey(message), (counts.get(historyMessageKey(message)) ?? 0) + 1);
6761
+ });
6762
+ for (const message of known.filter(generated)) {
6763
+ const value = historyMessageKey(message);
6764
+ const remaining = counts.get(value) ?? 0;
6765
+ if (remaining)
6766
+ counts.set(value, remaining - 1);
6767
+ else
6768
+ metadata.push(message);
6769
+ }
6770
+ return [...startsWithUser ? [mergeHistoryMetadata(known[0], native[0])] : [], ...messages, ...metadata];
6771
+ };
6772
+ try {
6773
+ const prompt = (turn) => turn[0]?.role === "user" ? `user:${submittedPrompt(turn[0])}` : "prefix";
6774
+ const turns = mergeOrderedHistory(splitHistoryTurns(input.knownMessages), splitHistoryTurns(input.nativeMessages), prompt, mergeTurn, (known, native) => historyTurnEvidence(known, native, prompt, (turn) => combineTextRuns(turn.slice(1).filter((message) => !generated(message))).map(key)));
6775
+ const messages = turns.flat().map((message, index) => message.index === undefined ? message : { ...message, index });
6776
+ return { kind: "recovered", messages };
6777
+ } catch (error) {
6778
+ if (error instanceof HistoryConflict)
6779
+ return { kind: "conflict", category: error.message };
6780
+ throw error;
6781
+ }
6782
+ };
6601
6783
  // ../pstdio-api-contracts/src/projects.ts
6602
6784
  var extensionSetupWarningSchema = object({
6603
6785
  code: literal("extension_setup_failed"),
@@ -6863,9 +7045,21 @@ var followUpDecisionSchema = union([
6863
7045
  var followUpResponseSchema = sessionSchema.extend({
6864
7046
  follow_up: followUpDecisionSchema
6865
7047
  });
7048
+ var sessionHistoryIssueSchema = object({
7049
+ code: _enum(["reconciliation_conflict", "checkpoint_unreadable", "native_unavailable"]),
7050
+ category: string2()
7051
+ });
7052
+ var sessionQueuedMessagesResponseSchema = object({ messages: array(sessionMessageSchema) });
7053
+ var sessionConversationSourcesSchema = object({
7054
+ checkpoint: array(sessionMessageSchema).nullable(),
7055
+ native: array(sessionMessageSchema).nullable(),
7056
+ checkpointError: string2().nullable(),
7057
+ nativeError: string2().nullable()
7058
+ });
6866
7059
  var sessionConversationResponseSchema = object({
7060
+ historyIssue: sessionHistoryIssueSchema.optional(),
6867
7061
  session: sessionSchema,
6868
- messages: array(unknown())
7062
+ messages: array(sessionMessageSchema)
6869
7063
  });
6870
7064
  var resolveSessionIdInputSchema = object({
6871
7065
  agent: string2(),
@@ -6959,6 +7153,9 @@ var workspaceProviderErrorSchema = object({
6959
7153
  retryable: boolean2(),
6960
7154
  occurred_at: string2()
6961
7155
  });
7156
+ var workspaceAnchorSchema = extensionResourceRefSchema.extend({
7157
+ role: _enum(["primary", "context", "source", "result"]).optional()
7158
+ });
6962
7159
  var workspaceSchema = object({
6963
7160
  id: string2(),
6964
7161
  project_id: string2(),
@@ -6979,7 +7176,7 @@ var workspaceSchema = object({
6979
7176
  archived: boolean2(),
6980
7177
  workspace_shorthand: string2(),
6981
7178
  startup_log_file_id: string2().nullable(),
6982
- anchors_json: array(extensionResourceRefSchema),
7179
+ anchors_json: array(workspaceAnchorSchema),
6983
7180
  created_at: string2(),
6984
7181
  updated_at: string2(),
6985
7182
  deleted_at: string2().nullable()
@@ -6989,7 +7186,9 @@ var createWorkspaceInputSchema = object({
6989
7186
  provider_id: string2().optional(),
6990
7187
  params: jsonObjectSchema.optional(),
6991
7188
  repo_id: string2().optional(),
6992
- base: string2().optional()
7189
+ base: string2().optional(),
7190
+ anchors: array(workspaceAnchorSchema).optional(),
7191
+ shorthand_base: string2().trim().min(1).optional()
6993
7192
  });
6994
7193
  var renameWorkspaceInputSchema = object({
6995
7194
  name: string2().trim().min(1, "Workspace name is required").max(120, "Workspace name must be 120 characters or less")
@@ -7032,6 +7231,13 @@ var listWorkspaceFilesInputSchema = object({
7032
7231
  query: string2().optional(),
7033
7232
  limit: number2().int().positive().max(500).optional()
7034
7233
  });
7234
+ var workspaceProviderDescriptorSchema = object({
7235
+ id: string2(),
7236
+ label: localizableStringSchema,
7237
+ icon: string2().optional(),
7238
+ description: localizableStringSchema.optional(),
7239
+ params: extensionParamObjectSchema
7240
+ });
7035
7241
  export {
7036
7242
  sessionAttachmentMimeTypesByExtension
7037
7243
  };