@stigmer/sdk 3.12.6 → 3.12.7

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.
Files changed (50) hide show
  1. package/__tests__/update-input-roundtrip.test.js +4 -0
  2. package/__tests__/update-input-roundtrip.test.js.map +1 -1
  3. package/execution/__tests__/transcript.test.d.ts +2 -0
  4. package/execution/__tests__/transcript.test.d.ts.map +1 -0
  5. package/execution/__tests__/transcript.test.js +527 -0
  6. package/execution/__tests__/transcript.test.js.map +1 -0
  7. package/execution/conversation-rules.d.ts +64 -0
  8. package/execution/conversation-rules.d.ts.map +1 -0
  9. package/execution/conversation-rules.js +113 -0
  10. package/execution/conversation-rules.js.map +1 -0
  11. package/execution/transcript.d.ts +171 -0
  12. package/execution/transcript.d.ts.map +1 -0
  13. package/execution/transcript.js +484 -0
  14. package/execution/transcript.js.map +1 -0
  15. package/gen/agentexecution.d.ts +11 -0
  16. package/gen/agentexecution.d.ts.map +1 -1
  17. package/gen/agentexecution.js +30 -1
  18. package/gen/agentexecution.js.map +1 -1
  19. package/gen/client.d.ts +5 -1
  20. package/gen/client.d.ts.map +1 -1
  21. package/gen/client.js +4 -0
  22. package/gen/client.js.map +1 -1
  23. package/gen/identityaccount.d.ts +1 -0
  24. package/gen/identityaccount.d.ts.map +1 -1
  25. package/gen/identityaccount.js +2 -0
  26. package/gen/identityaccount.js.map +1 -1
  27. package/gen/memory.d.ts +58 -0
  28. package/gen/memory.d.ts.map +1 -0
  29. package/gen/memory.js +143 -0
  30. package/gen/memory.js.map +1 -0
  31. package/gen/organization.d.ts +1 -0
  32. package/gen/organization.d.ts.map +1 -1
  33. package/gen/organization.js +2 -0
  34. package/gen/organization.js.map +1 -1
  35. package/index.d.ts +3 -0
  36. package/index.d.ts.map +1 -1
  37. package/index.js +3 -0
  38. package/index.js.map +1 -1
  39. package/package.json +2 -2
  40. package/src/__tests__/update-input-roundtrip.test.ts +4 -0
  41. package/src/execution/__tests__/transcript.golden.md +100 -0
  42. package/src/execution/__tests__/transcript.test.ts +627 -0
  43. package/src/execution/conversation-rules.ts +119 -0
  44. package/src/execution/transcript.ts +735 -0
  45. package/src/gen/agentexecution.ts +45 -1
  46. package/src/gen/client.ts +6 -1
  47. package/src/gen/identityaccount.ts +3 -0
  48. package/src/gen/memory.ts +164 -0
  49. package/src/gen/organization.ts +3 -0
  50. package/src/index.ts +28 -0
@@ -0,0 +1,119 @@
1
+ // Framework-agnostic conversation-assembly rules for every Stigmer surface.
2
+ //
3
+ // A session's conversation is reassembled from its AgentExecution list by
4
+ // several independent consumers: the React thread (@stigmer/react
5
+ // buildThreadItems / useSessionConversation), the CLI's session replay
6
+ // (snapshotToEvents), and the canonical transcript assembler (transcript.ts).
7
+ // Before this module each consumer carried its own copy of the same small
8
+ // rules, and the copies had already drifted (the CLI's replay lacks the
9
+ // Build-from-plan skip). These are the shared, canonical rules; presentation
10
+ // concerns (plan cards, todos anchoring, event interleaving) stay in the
11
+ // consumers.
12
+ //
13
+ // This module has no React or framework dependency so it can be shared by
14
+ // @stigmer/react, @stigmer/ink, and the CLI.
15
+
16
+ import type { AgentExecution } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
17
+
18
+ /**
19
+ * Returns the executions in chronological (oldest-first) order — the order a
20
+ * conversation reads top-to-bottom.
21
+ *
22
+ * Defense-in-depth against an unordered list response: the executions ARE the
23
+ * transcript, so a scrambled order drops the newest turns out of view (they no
24
+ * longer sort to the bottom). The server orders this list, but consumers must
25
+ * never depend on that alone. Resource ids are time-sortable ULIDs
26
+ * (`aex_01k…`), so an ascending id sort is creation order without parsing
27
+ * timestamps; entries missing an id sort last but keep a stable relative order.
28
+ */
29
+ export function sortChronologically(
30
+ executions: readonly AgentExecution[],
31
+ ): AgentExecution[] {
32
+ return [...executions].sort((a, b) => {
33
+ const aId = a.metadata?.id ?? "";
34
+ const bId = b.metadata?.id ?? "";
35
+ if (aId === bId) return 0;
36
+ if (!aId) return 1;
37
+ if (!bId) return -1;
38
+ return aId < bId ? -1 : 1;
39
+ });
40
+ }
41
+
42
+ /**
43
+ * Collects the ids of executions replaced via edit-and-resubmit.
44
+ *
45
+ * The successor execution carries `spec.supersedes_execution_id`; hiding the
46
+ * superseded turn makes the edited message read as a single corrected exchange
47
+ * (in-place replace). The raw execution list still contains superseded records
48
+ * — execution-history surfaces show them deliberately — so this is a
49
+ * conversation-view rule, applied by whoever renders or exports a
50
+ * conversation.
51
+ *
52
+ * @param extraSupersededId An id known only outside the list — the live stream
53
+ * copy's `supersedesExecutionId`: right after a resubmit, the successor
54
+ * streams before the list refetch delivers it.
55
+ */
56
+ export function supersededExecutionIds(
57
+ executions: readonly AgentExecution[],
58
+ extraSupersededId?: string | null,
59
+ ): Set<string> {
60
+ const ids = new Set<string>();
61
+ for (const e of executions) {
62
+ const superseded = e.spec?.supersedesExecutionId;
63
+ if (superseded) ids.add(superseded);
64
+ }
65
+ if (extraSupersededId) ids.add(extraSupersededId);
66
+ return ids;
67
+ }
68
+
69
+ /**
70
+ * `true` when the execution is a Build-from-plan turn.
71
+ *
72
+ * Such a turn's `spec.message` is a machine-written label ("Build from
73
+ * plan"), not user prose — the real instruction is runner-injected from the
74
+ * same flag. Rendering or exporting the label as a user message would
75
+ * attribute words to the user they never typed; the plan the turn builds from
76
+ * is the visible cause.
77
+ */
78
+ export function isBuildFromPlanTurn(exec: AgentExecution): boolean {
79
+ return exec.spec?.executionConfig?.buildFromPlan === true;
80
+ }
81
+
82
+ /**
83
+ * The user prose that opens an execution's turn, or `null` when the turn has
84
+ * none.
85
+ *
86
+ * `spec.message` is the submitted prompt, but three shapes of it are not user
87
+ * prose and synthesize no user turn:
88
+ * - empty — nothing was typed (programmatic creates);
89
+ * - the literal `"execute"` — the legacy placeholder stamped on runs started
90
+ * without a message;
91
+ * - a Build-from-plan turn's machine-written label (see
92
+ * {@link isBuildFromPlanTurn}).
93
+ */
94
+ export function syntheticUserPrompt(exec: AgentExecution): string | null {
95
+ const specMessage = exec.spec?.message;
96
+ if (!specMessage || specMessage === "execute" || isBuildFromPlanTurn(exec)) {
97
+ return null;
98
+ }
99
+ return specMessage;
100
+ }
101
+
102
+ /**
103
+ * Extracts the execution id from an artifact storage key of the form
104
+ * `artifacts/{executionId}/...`. Returns `null` for an unexpected shape so the
105
+ * caller skips the fetch rather than issuing a request the server would
106
+ * reject.
107
+ *
108
+ * The fetch id must always derive from the key, never from render or export
109
+ * context: offloaded outputs inside a sub-agent's transcript are stored under
110
+ * the PARENT execution's id (the execution whose status was persisted), and
111
+ * the key is the record of that.
112
+ */
113
+ export function execIdFromStorageKey(storageKey: string): string | null {
114
+ const parts = storageKey.split("/");
115
+ if (parts.length >= 3 && parts[0] === "artifacts" && parts[1]) {
116
+ return parts[1];
117
+ }
118
+ return null;
119
+ }