@anthusai/papyrus 5.43.0-next.43 → 5.43.0-next.45

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.
@@ -1431,7 +1431,9 @@ async function appendMessageToThread(thread: MessageThreadRecord, build: (sequen
1431
1431
  list: () => listMessagesByThreadId(threadId, 1000),
1432
1432
  create: (sequenceNumber) => {
1433
1433
  now = new Date().toISOString();
1434
- built = build(sequenceNumber, now);
1434
+ // `createdAt` is part of the composite sort key of messagesByResponseTargetStatusCreatedAt, so AppSync refuses a Message that
1435
+ // sets it without `responseStatus`. A person's message waits on no responder: COMPLETED, as the Python message contract writes.
1436
+ built = { responseStatus: "COMPLETED", ...build(sequenceNumber, now) };
1435
1437
  return runGraphql<{ createMessage?: MessageRecord | null }>(CREATE_MESSAGE_MUTATION, { input: built });
1436
1438
  },
1437
1439
  });
@@ -1905,10 +1907,17 @@ async function runGraphql<T extends Record<string, unknown>>(
1905
1907
  const client = generateClient<Schema>() as unknown as {
1906
1908
  graphql: (options: Record<string, unknown>) => Promise<{ data?: T | null; errors?: unknown[] | null }>;
1907
1909
  };
1910
+ // The Amplify client rejects with the raw `{ data, errors }` response when the API returns errors; turn that into an
1911
+ // Error carrying the API's own words, so a refusal is said in words instead of as an unknown failure.
1908
1912
  const response = await client.graphql({
1909
1913
  query,
1910
1914
  variables,
1911
1915
  authMode: USER_POOL_AUTH_MODE,
1916
+ }).catch((failure: unknown) => {
1917
+ if (failure && typeof failure === "object" && Array.isArray((failure as { errors?: unknown }).errors)) {
1918
+ assertNoGraphQLErrors((failure as { errors: unknown[] }).errors);
1919
+ }
1920
+ throw failure;
1912
1921
  });
1913
1922
  assertNoGraphQLErrors(response.errors);
1914
1923
  return (response.data ?? {}) as T;
@@ -4,7 +4,7 @@ import { generateClient } from "aws-amplify/data";
4
4
  import type { Schema } from "../amplify/data/resource";
5
5
  import { isLocalPreviewEnabled } from "../lib/local-preview";
6
6
  import { createLocalPreviewReportingTrail, createLocalPreviewStoriesForReference } from "../lib/local-preview-seed";
7
- import { EMPTY_STORY_REPORTING_TRAIL, referencesByRole, siteNameOf, type ReferenceStory, type StoryReportingTrail, type TrailAssignment, type TrailAttachment, type TrailReference, type TrailThread, type TrailVersion } from "../lib/story-reporting-trail";
7
+ import { EMPTY_STORY_REPORTING_TRAIL, referencesByRole, siteNameOf, stageMovesOf, type ReferenceStory, type StoryReportingTrail, type TrailAssignment, type TrailAttachment, type TrailReference, type TrailThread, type TrailVersion } from "../lib/story-reporting-trail";
8
8
  import { configureAmplifyClient } from "./amplify-client-provider";
9
9
 
10
10
  type GraphqlResponse = { data?: Record<string, { items?: Record<string, unknown>[] | null; nextToken?: string | null } | null> | null; errors?: { message?: string }[] | null };
@@ -89,7 +89,7 @@ export async function loadStoryReportingTrail(lineageId: string, itemId: string,
89
89
  configureAmplifyClient();
90
90
  const client = generateClient<Schema>() as unknown as GraphqlClient;
91
91
  const [versionRows, assignmentRows, attachmentRows, relationRows] = await Promise.all([
92
- listAll(client, "listItemsByLineageAndVersion", "id versionNumber versionState status stage headline title changeReason versionCreatedBy versionCreatedAt updatedAt", ", $lineageId: ID!", ", lineageId: $lineageId", { lineageId }).catch(() => []),
92
+ listAll(client, "listItemsByLineageAndVersion", "id versionNumber versionState status stage headline title changeReason versionCreatedBy versionCreatedAt updatedAt metadata", ", $lineageId: ID!", ", lineageId: $lineageId", { lineageId }).catch(() => []),
93
93
  listAll(client, "listAssignmentsByItemLineageAndCreatedAt", "id title assignmentTypeKey status assigneeKey summary createdAt", ", $itemLineageId: ID!", ", itemLineageId: $itemLineageId", { itemLineageId: lineageId }).catch(() => []),
94
94
  listAll(client, "listModelAttachmentsByOwnerRoleAndSortKey", "id role filename mediaType byteSize", ", $ownerId: ID!", ", ownerId: $ownerId", { ownerId: itemId }).catch(() => []),
95
95
  listAll(client, "listSemanticRelations", "id predicate subjectKind subjectLineageId objectKind objectId objectLineageId metadata", ", $filter: ModelSemanticRelationFilterInput", ", filter: $filter", { filter: { or: [{ subjectLineageId: { eq: lineageId } }, { objectLineageId: { eq: lineageId } }] } }).catch(() => []),
@@ -124,5 +124,6 @@ export async function loadStoryReportingTrail(lineageId: string, itemId: string,
124
124
  };
125
125
  })));
126
126
  const attachments: TrailAttachment[] = attachmentRows.map(row => ({ id: String(row.id), role: text(row.role), filename: text(row.filename), mediaType: text(row.mediaType), byteSize: typeof row.byteSize === "number" ? row.byteSize : null }));
127
- return { ...EMPTY_STORY_REPORTING_TRAIL, versions, assignments, threads, references, attachments };
127
+ const latestRow = [...versionRows].sort((left, right) => Number(right.versionNumber ?? 0) - Number(left.versionNumber ?? 0))[0];
128
+ return { ...EMPTY_STORY_REPORTING_TRAIL, versions, assignments, threads, references, attachments, stageMoves: stageMovesOf(latestRow?.metadata) };
128
129
  }
@@ -91,6 +91,14 @@ export function StoryReportingTrailView({ citedKeys = [], demo = false, focusedR
91
91
  </li>)}
92
92
  </ol>
93
93
  </TrailSection>
94
+ {trail.stageMoves.length > 0 && <TrailSection count={trail.stageMoves.length} title="Stage moves">
95
+ <ol className="story-trail-list" data-story-trail-stage-moves>
96
+ {[...trail.stageMoves].reverse().map(move => <li key={`${move.at}-${move.to}`}>
97
+ <div className="story-trail-row"><strong>{move.from ?? "start"} → {move.to}</strong>{move.by && <span className="story-trail-chip">{move.by}</span>}<span className="story-trail-meta">{formatTimestamp(move.at)}</span></div>
98
+ {move.reason && <p className="story-trail-meta">{move.reason}</p>}
99
+ </li>)}
100
+ </ol>
101
+ </TrailSection>}
94
102
  <TrailSection count={trail.versions.length} title="Draft versions">
95
103
  <ol className="story-trail-list">
96
104
  {trail.versions.map((version, versionIndex) => <li key={version.id}>
@@ -17,6 +17,15 @@ export type TrailVersion = {
17
17
  updatedAt: string | null;
18
18
  };
19
19
 
20
+ /** One stage move of the story, read from the latest Item version's `metadata.stageMoves` (not from Messages). */
21
+ export type TrailStageMove = {
22
+ at: string;
23
+ from: string | null;
24
+ to: string;
25
+ by: string | null;
26
+ reason: string | null;
27
+ };
28
+
20
29
  export type TrailAssignmentEvent = {
21
30
  id: string;
22
31
  eventType: string;
@@ -89,12 +98,29 @@ export type StoryReportingTrail = {
89
98
  threads: TrailThread[];
90
99
  references: TrailReference[];
91
100
  attachments: TrailAttachment[];
101
+ stageMoves: TrailStageMove[];
92
102
  };
93
103
 
94
- export const EMPTY_STORY_REPORTING_TRAIL: StoryReportingTrail = { versions: [], assignments: [], threads: [], references: [], attachments: [] };
104
+ export const EMPTY_STORY_REPORTING_TRAIL: StoryReportingTrail = { versions: [], assignments: [], threads: [], references: [], attachments: [], stageMoves: [] };
105
+
106
+ /** The stage moves recorded on an Item version's metadata, oldest first. Anything that is not a well-formed move is skipped. */
107
+ export function stageMovesOf(metadata: unknown): TrailStageMove[] {
108
+ let parsed: unknown = metadata;
109
+ if (typeof metadata === "string") {
110
+ try { parsed = JSON.parse(metadata); } catch { return []; }
111
+ }
112
+ const moves = typeof parsed === "object" && parsed ? (parsed as Record<string, unknown>).stageMoves : null;
113
+ if (!Array.isArray(moves)) return [];
114
+ return moves.flatMap(entry => {
115
+ if (typeof entry !== "object" || !entry) return [];
116
+ const move = entry as Record<string, unknown>;
117
+ if (typeof move.at !== "string" || typeof move.to !== "string") return [];
118
+ return [{ at: move.at, from: typeof move.from === "string" ? move.from : null, to: move.to, by: typeof move.by === "string" ? move.by : null, reason: typeof move.reason === "string" ? move.reason : null }];
119
+ });
120
+ }
95
121
 
96
122
  export function storyReportingTrailIsEmpty(trail: StoryReportingTrail): boolean {
97
- return trail.assignments.length === 0 && trail.threads.length === 0 && trail.references.length === 0 && trail.attachments.length === 0 && trail.versions.length <= 1;
123
+ return trail.assignments.length === 0 && trail.threads.length === 0 && trail.stageMoves.length === 0 && trail.references.length === 0 && trail.attachments.length === 0 && trail.versions.length <= 1;
98
124
  }
99
125
 
100
126
  export type StoryFeedEntry =
package/package.json CHANGED
@@ -516,7 +516,7 @@
516
516
  "optional": true
517
517
  }
518
518
  },
519
- "version": "5.43.0-next.43",
519
+ "version": "5.43.0-next.45",
520
520
  "dependencies": {
521
521
  "@aws-amplify/adapter-nextjs": "^1.7.3",
522
522
  "@aws-crypto/sha256-js": "5.2.0",
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "5.43.0-next.43",
2
+ "version": "5.43.0-next.45",
3
3
  "routes": [
4
4
  {
5
5
  "file": "app/%5F_preview/[[...path]]/route.ts",