@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.
- package/__tests__/update-input-roundtrip.test.js +4 -0
- package/__tests__/update-input-roundtrip.test.js.map +1 -1
- package/execution/__tests__/transcript.test.d.ts +2 -0
- package/execution/__tests__/transcript.test.d.ts.map +1 -0
- package/execution/__tests__/transcript.test.js +527 -0
- package/execution/__tests__/transcript.test.js.map +1 -0
- package/execution/conversation-rules.d.ts +64 -0
- package/execution/conversation-rules.d.ts.map +1 -0
- package/execution/conversation-rules.js +113 -0
- package/execution/conversation-rules.js.map +1 -0
- package/execution/transcript.d.ts +171 -0
- package/execution/transcript.d.ts.map +1 -0
- package/execution/transcript.js +484 -0
- package/execution/transcript.js.map +1 -0
- package/gen/agentexecution.d.ts +11 -0
- package/gen/agentexecution.d.ts.map +1 -1
- package/gen/agentexecution.js +30 -1
- package/gen/agentexecution.js.map +1 -1
- package/gen/client.d.ts +5 -1
- package/gen/client.d.ts.map +1 -1
- package/gen/client.js +4 -0
- package/gen/client.js.map +1 -1
- package/gen/identityaccount.d.ts +1 -0
- package/gen/identityaccount.d.ts.map +1 -1
- package/gen/identityaccount.js +2 -0
- package/gen/identityaccount.js.map +1 -1
- package/gen/memory.d.ts +58 -0
- package/gen/memory.d.ts.map +1 -0
- package/gen/memory.js +143 -0
- package/gen/memory.js.map +1 -0
- package/gen/organization.d.ts +1 -0
- package/gen/organization.d.ts.map +1 -1
- package/gen/organization.js +2 -0
- package/gen/organization.js.map +1 -1
- package/index.d.ts +3 -0
- package/index.d.ts.map +1 -1
- package/index.js +3 -0
- package/index.js.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/update-input-roundtrip.test.ts +4 -0
- package/src/execution/__tests__/transcript.golden.md +100 -0
- package/src/execution/__tests__/transcript.test.ts +627 -0
- package/src/execution/conversation-rules.ts +119 -0
- package/src/execution/transcript.ts +735 -0
- package/src/gen/agentexecution.ts +45 -1
- package/src/gen/client.ts +6 -1
- package/src/gen/identityaccount.ts +3 -0
- package/src/gen/memory.ts +164 -0
- package/src/gen/organization.ts +3 -0
- 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
|
+
}
|