@elevasis/sdk 1.38.0 → 1.39.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/dist/cli.cjs +1 -1
- package/dist/index.d.ts +20 -0
- package/dist/node/index.d.ts +20 -0
- package/dist/test-utils/index.d.ts +22 -1
- package/dist/test-utils/index.js +70 -32
- package/dist/types/worker/index.d.ts +2 -1
- package/dist/worker/index.js +70 -32
- package/package.json +4 -4
- package/reference/claude-config/sync-notes/2026-07-23-agent-session-memory.md +49 -0
- package/reference/claude-config/sync-notes/2026-07-23-workos-org-marker.md +50 -0
package/dist/cli.cjs
CHANGED
|
@@ -45850,7 +45850,7 @@ function wrapAction(commandName, fn) {
|
|
|
45850
45850
|
// package.json
|
|
45851
45851
|
var package_default = {
|
|
45852
45852
|
name: "@elevasis/sdk",
|
|
45853
|
-
version: "1.
|
|
45853
|
+
version: "1.39.0",
|
|
45854
45854
|
description: "SDK for building Elevasis organization resources",
|
|
45855
45855
|
type: "module",
|
|
45856
45856
|
bin: {
|
package/dist/index.d.ts
CHANGED
|
@@ -11239,6 +11239,17 @@ type MessageEvent = {
|
|
|
11239
11239
|
result?: unknown;
|
|
11240
11240
|
error?: string;
|
|
11241
11241
|
};
|
|
11242
|
+
/**
|
|
11243
|
+
* A message from an earlier turn of this session.
|
|
11244
|
+
*
|
|
11245
|
+
* Deliberately lean (no ids, timestamps, or event metadata): this crosses the
|
|
11246
|
+
* parent -> worker payload boundary and is replayed verbatim into the model's message
|
|
11247
|
+
* array, so it carries only what the model needs to read the conversation.
|
|
11248
|
+
*/
|
|
11249
|
+
interface ConversationMessage {
|
|
11250
|
+
role: 'user' | 'assistant';
|
|
11251
|
+
content: string;
|
|
11252
|
+
}
|
|
11242
11253
|
/**
|
|
11243
11254
|
* Execution context for all resources
|
|
11244
11255
|
* Unified callback replaces SessionTurnMessages (removed)
|
|
@@ -11246,6 +11257,15 @@ type MessageEvent = {
|
|
|
11246
11257
|
interface ExecutionContext extends ExecutionMetadata {
|
|
11247
11258
|
logger: IExecutionLogger;
|
|
11248
11259
|
signal?: AbortSignal;
|
|
11260
|
+
/**
|
|
11261
|
+
* This session's earlier turns, oldest first, as actually said.
|
|
11262
|
+
*
|
|
11263
|
+
* A session agent is handed its own conversation: these are replayed into the model's
|
|
11264
|
+
* message array as real user/assistant turns. Absent for one-off (non-session)
|
|
11265
|
+
* executions. The session layer bounds this before it is sent -- see
|
|
11266
|
+
* `selectConversationHistory`.
|
|
11267
|
+
*/
|
|
11268
|
+
conversationHistory?: ConversationMessage[];
|
|
11249
11269
|
onMessageEvent?: (event: MessageEvent) => Promise<void>;
|
|
11250
11270
|
/** Called per iteration to write heartbeat + check stall status. Non-fatal if it throws. */
|
|
11251
11271
|
onHeartbeat?: () => Promise<void>;
|
package/dist/node/index.d.ts
CHANGED
|
@@ -2760,6 +2760,17 @@ type MessageEvent = {
|
|
|
2760
2760
|
result?: unknown;
|
|
2761
2761
|
error?: string;
|
|
2762
2762
|
};
|
|
2763
|
+
/**
|
|
2764
|
+
* A message from an earlier turn of this session.
|
|
2765
|
+
*
|
|
2766
|
+
* Deliberately lean (no ids, timestamps, or event metadata): this crosses the
|
|
2767
|
+
* parent -> worker payload boundary and is replayed verbatim into the model's message
|
|
2768
|
+
* array, so it carries only what the model needs to read the conversation.
|
|
2769
|
+
*/
|
|
2770
|
+
interface ConversationMessage {
|
|
2771
|
+
role: 'user' | 'assistant';
|
|
2772
|
+
content: string;
|
|
2773
|
+
}
|
|
2763
2774
|
/**
|
|
2764
2775
|
* Execution context for all resources
|
|
2765
2776
|
* Unified callback replaces SessionTurnMessages (removed)
|
|
@@ -2767,6 +2778,15 @@ type MessageEvent = {
|
|
|
2767
2778
|
interface ExecutionContext extends ExecutionMetadata {
|
|
2768
2779
|
logger: IExecutionLogger;
|
|
2769
2780
|
signal?: AbortSignal;
|
|
2781
|
+
/**
|
|
2782
|
+
* This session's earlier turns, oldest first, as actually said.
|
|
2783
|
+
*
|
|
2784
|
+
* A session agent is handed its own conversation: these are replayed into the model's
|
|
2785
|
+
* message array as real user/assistant turns. Absent for one-off (non-session)
|
|
2786
|
+
* executions. The session layer bounds this before it is sent -- see
|
|
2787
|
+
* `selectConversationHistory`.
|
|
2788
|
+
*/
|
|
2789
|
+
conversationHistory?: ConversationMessage[];
|
|
2770
2790
|
onMessageEvent?: (event: MessageEvent) => Promise<void>;
|
|
2771
2791
|
/** Called per iteration to write heartbeat + check stall status. Non-fatal if it throws. */
|
|
2772
2792
|
onHeartbeat?: () => Promise<void>;
|
|
@@ -10313,6 +10313,17 @@ type MessageEvent = {
|
|
|
10313
10313
|
result?: unknown;
|
|
10314
10314
|
error?: string;
|
|
10315
10315
|
};
|
|
10316
|
+
/**
|
|
10317
|
+
* A message from an earlier turn of this session.
|
|
10318
|
+
*
|
|
10319
|
+
* Deliberately lean (no ids, timestamps, or event metadata): this crosses the
|
|
10320
|
+
* parent -> worker payload boundary and is replayed verbatim into the model's message
|
|
10321
|
+
* array, so it carries only what the model needs to read the conversation.
|
|
10322
|
+
*/
|
|
10323
|
+
interface ConversationMessage {
|
|
10324
|
+
role: 'user' | 'assistant';
|
|
10325
|
+
content: string;
|
|
10326
|
+
}
|
|
10316
10327
|
/**
|
|
10317
10328
|
* Execution context for all resources
|
|
10318
10329
|
* Unified callback replaces SessionTurnMessages (removed)
|
|
@@ -10320,6 +10331,15 @@ type MessageEvent = {
|
|
|
10320
10331
|
interface ExecutionContext extends ExecutionMetadata {
|
|
10321
10332
|
logger: IExecutionLogger;
|
|
10322
10333
|
signal?: AbortSignal;
|
|
10334
|
+
/**
|
|
10335
|
+
* This session's earlier turns, oldest first, as actually said.
|
|
10336
|
+
*
|
|
10337
|
+
* A session agent is handed its own conversation: these are replayed into the model's
|
|
10338
|
+
* message array as real user/assistant turns. Absent for one-off (non-session)
|
|
10339
|
+
* executions. The session layer bounds this before it is sent -- see
|
|
10340
|
+
* `selectConversationHistory`.
|
|
10341
|
+
*/
|
|
10342
|
+
conversationHistory?: ConversationMessage[];
|
|
10323
10343
|
onMessageEvent?: (event: MessageEvent) => Promise<void>;
|
|
10324
10344
|
/** Called per iteration to write heartbeat + check stall status. Non-fatal if it throws. */
|
|
10325
10345
|
onHeartbeat?: () => Promise<void>;
|
|
@@ -10793,7 +10813,8 @@ type TypedAdapter<TMap extends ToolMethodMap$1> = {
|
|
|
10793
10813
|
* humanCheckpoints?: [...], relationships?: {...} }
|
|
10794
10814
|
*
|
|
10795
10815
|
* Parent -> Worker: { type: 'execute', resourceId, executionId, input, organizationId?, organizationName?,
|
|
10796
|
-
* sessionId?, sessionTurnNumber?,
|
|
10816
|
+
* sessionId?, sessionTurnNumber?, sessionMemory?, conversationHistory?,
|
|
10817
|
+
* parentExecutionId?, executionDepth }
|
|
10797
10818
|
* Worker -> Parent: { type: 'result', status, output?, memorySnapshot?, error?, logs, metrics: { durationMs } }
|
|
10798
10819
|
*
|
|
10799
10820
|
* Parent -> Worker: { type: 'abort' } (graceful abort before terminate)
|
package/dist/test-utils/index.js
CHANGED
|
@@ -4768,6 +4768,8 @@ function buildReasoningRequest(iterationContext) {
|
|
|
4768
4768
|
iterationContext.iteration,
|
|
4769
4769
|
iterationContext.executionContext.sessionTurnNumber
|
|
4770
4770
|
),
|
|
4771
|
+
// A session agent gets its own conversation. Non-session executions have none.
|
|
4772
|
+
conversationHistory: iterationContext.executionContext.conversationHistory ?? [],
|
|
4771
4773
|
includeMessageAction: isSessionCapable,
|
|
4772
4774
|
includeNavigateKnowledge: hasKnowledgeMap,
|
|
4773
4775
|
includeMemoryOps
|
|
@@ -5214,12 +5216,16 @@ function validateTokenConfiguration(model, maxOutputTokens) {
|
|
|
5214
5216
|
);
|
|
5215
5217
|
}
|
|
5216
5218
|
}
|
|
5219
|
+
function buildAgentMessages(systemPrompt, memoryContext, conversationHistory = []) {
|
|
5220
|
+
return [
|
|
5221
|
+
{ role: "system", content: systemPrompt },
|
|
5222
|
+
...conversationHistory.map(({ role, content }) => ({ role, content })),
|
|
5223
|
+
{ role: "user", content: memoryContext }
|
|
5224
|
+
];
|
|
5225
|
+
}
|
|
5217
5226
|
async function callLLMForAgentIteration(adapter, request) {
|
|
5218
5227
|
validateTokenConfiguration(request.model, request.constraints.maxOutputTokens);
|
|
5219
|
-
const messages =
|
|
5220
|
-
{ role: "system", content: request.systemPrompt },
|
|
5221
|
-
{ role: "user", content: request.memoryContext }
|
|
5222
|
-
];
|
|
5228
|
+
const messages = buildAgentMessages(request.systemPrompt, request.memoryContext, request.conversationHistory);
|
|
5223
5229
|
const response = await adapter.generate({
|
|
5224
5230
|
messages,
|
|
5225
5231
|
responseSchema: buildIterationResponseSchema(
|
|
@@ -5248,10 +5254,7 @@ async function callLLMForAgentIteration(adapter, request) {
|
|
|
5248
5254
|
async function callLLMForAgentCompletion(adapter, request) {
|
|
5249
5255
|
validateTokenConfiguration(request.model, request.constraints.maxOutputTokens);
|
|
5250
5256
|
const response = await adapter.generate({
|
|
5251
|
-
messages:
|
|
5252
|
-
{ role: "system", content: request.systemPrompt },
|
|
5253
|
-
{ role: "user", content: request.memoryContext }
|
|
5254
|
-
],
|
|
5257
|
+
messages: buildAgentMessages(request.systemPrompt, request.memoryContext, request.conversationHistory),
|
|
5255
5258
|
responseSchema: request.outputSchema,
|
|
5256
5259
|
// Use output schema directly
|
|
5257
5260
|
temperature: request.constraints.temperature || 0.3,
|
|
@@ -5385,6 +5388,7 @@ async function processReasoning(iterationContext) {
|
|
|
5385
5388
|
const { reasoning, memoryOps, nextActions } = await callLLMForAgentIteration(adapter, {
|
|
5386
5389
|
systemPrompt: request.systemPrompt,
|
|
5387
5390
|
memoryContext: request.memoryContext,
|
|
5391
|
+
conversationHistory: request.conversationHistory,
|
|
5388
5392
|
tools: request.tools,
|
|
5389
5393
|
constraints: request.constraints,
|
|
5390
5394
|
model: iterationContext.modelConfig.model,
|
|
@@ -6257,11 +6261,10 @@ var MemoryManager = class {
|
|
|
6257
6261
|
*/
|
|
6258
6262
|
toContext(currentIteration, currentTurn) {
|
|
6259
6263
|
const status = this.getStatus();
|
|
6260
|
-
const
|
|
6261
|
-
|
|
6262
|
-
).reverse();
|
|
6264
|
+
const inTurnScope = (entry) => !currentTurn || entry.turnNumber === currentTurn || entry.turnNumber == null;
|
|
6265
|
+
const currentContext = this.memory.history.filter((entry) => inTurnScope(entry) && entry.iterationNumber === currentIteration).reverse();
|
|
6263
6266
|
const earlierContext = this.memory.history.filter(
|
|
6264
|
-
(entry) => (
|
|
6267
|
+
(entry) => inTurnScope(entry) && entry.iterationNumber !== null && entry.iterationNumber < currentIteration
|
|
6265
6268
|
);
|
|
6266
6269
|
const formatEntry = (entry) => {
|
|
6267
6270
|
const label = `[${entry.type.toUpperCase()}]`;
|
|
@@ -6338,6 +6341,9 @@ function initializeKnowledgeMap(knowledgeMap) {
|
|
|
6338
6341
|
nodes: Object.fromEntries(Object.entries(knowledgeMap.nodes).map(([id, node]) => [id, { ...node }]))
|
|
6339
6342
|
};
|
|
6340
6343
|
}
|
|
6344
|
+
function hasMemoryContent(memory) {
|
|
6345
|
+
return Object.keys(memory.sessionMemory).length > 0 || memory.history.length > 0;
|
|
6346
|
+
}
|
|
6341
6347
|
var Agent = class {
|
|
6342
6348
|
// Base properties from definition
|
|
6343
6349
|
config;
|
|
@@ -6347,6 +6353,7 @@ var Agent = class {
|
|
|
6347
6353
|
knowledgeMap;
|
|
6348
6354
|
definition;
|
|
6349
6355
|
adapterFactory;
|
|
6356
|
+
initialMemory;
|
|
6350
6357
|
// Derived properties (computed from definition)
|
|
6351
6358
|
shouldGenerateOutput;
|
|
6352
6359
|
// Runtime state (initialized during execution)
|
|
@@ -6361,10 +6368,12 @@ var Agent = class {
|
|
|
6361
6368
|
*
|
|
6362
6369
|
* @param definition - Agent definition with config, contract, tools, and optional preloadMemory
|
|
6363
6370
|
* @param adapterFactory - Factory for creating LLM adapters (decouples engine from provider SDKs)
|
|
6371
|
+
* @param options - Per-execution options (e.g. restored session memory)
|
|
6364
6372
|
*/
|
|
6365
|
-
constructor(definition, adapterFactory) {
|
|
6373
|
+
constructor(definition, adapterFactory, options = {}) {
|
|
6366
6374
|
this.definition = definition;
|
|
6367
6375
|
this.adapterFactory = adapterFactory;
|
|
6376
|
+
this.initialMemory = options.initialMemory;
|
|
6368
6377
|
this.config = definition.config;
|
|
6369
6378
|
this.contract = definition.contract;
|
|
6370
6379
|
this.modelConfig = definition.modelConfig;
|
|
@@ -6458,25 +6467,9 @@ var Agent = class {
|
|
|
6458
6467
|
* @returns Initialized MemoryManager instance
|
|
6459
6468
|
*/
|
|
6460
6469
|
async initializeMemoryManager(validatedInput, context) {
|
|
6461
|
-
|
|
6462
|
-
if (
|
|
6463
|
-
const preloadStartTime = Date.now();
|
|
6464
|
-
memory = await this.definition.preloadMemory(context);
|
|
6465
|
-
const preloadEndTime = Date.now();
|
|
6466
|
-
this.logger.action(
|
|
6467
|
-
"memory-preload",
|
|
6468
|
-
`Preloaded ${Object.keys(memory.sessionMemory).length} session memory entries`,
|
|
6469
|
-
0,
|
|
6470
|
-
preloadStartTime,
|
|
6471
|
-
preloadEndTime,
|
|
6472
|
-
preloadEndTime - preloadStartTime
|
|
6473
|
-
);
|
|
6470
|
+
const memory = await this.resolveInitialMemory(context);
|
|
6471
|
+
if (hasMemoryContent(memory)) {
|
|
6474
6472
|
await this.reloadKnowledgeMapTools(memory, context);
|
|
6475
|
-
} else {
|
|
6476
|
-
memory = {
|
|
6477
|
-
sessionMemory: {},
|
|
6478
|
-
history: []
|
|
6479
|
-
};
|
|
6480
6473
|
}
|
|
6481
6474
|
const inputStartTime = Date.now();
|
|
6482
6475
|
memory.history.push({
|
|
@@ -6497,6 +6490,44 @@ var Agent = class {
|
|
|
6497
6490
|
);
|
|
6498
6491
|
return new MemoryManager(memory, this.config.constraints, this.logger);
|
|
6499
6492
|
}
|
|
6493
|
+
/**
|
|
6494
|
+
* Resolve the memory this execution starts from.
|
|
6495
|
+
*
|
|
6496
|
+
* Precedence: caller-supplied `initialMemory` (session restore) > the definition's
|
|
6497
|
+
* `preloadMemory` author hook > empty. Returns a detached copy in the restore case so
|
|
6498
|
+
* the agent's mutations cannot corrupt the caller's snapshot.
|
|
6499
|
+
*/
|
|
6500
|
+
async resolveInitialMemory(context) {
|
|
6501
|
+
if (this.initialMemory) {
|
|
6502
|
+
const restoreStartTime = Date.now();
|
|
6503
|
+
const memory = structuredClone(this.initialMemory);
|
|
6504
|
+
const restoreEndTime = Date.now();
|
|
6505
|
+
this.logger.action(
|
|
6506
|
+
"memory-restore",
|
|
6507
|
+
`Restored ${Object.keys(memory.sessionMemory).length} session memory entries`,
|
|
6508
|
+
0,
|
|
6509
|
+
restoreStartTime,
|
|
6510
|
+
restoreEndTime,
|
|
6511
|
+
restoreEndTime - restoreStartTime
|
|
6512
|
+
);
|
|
6513
|
+
return memory;
|
|
6514
|
+
}
|
|
6515
|
+
if (this.definition.preloadMemory) {
|
|
6516
|
+
const preloadStartTime = Date.now();
|
|
6517
|
+
const memory = await this.definition.preloadMemory(context);
|
|
6518
|
+
const preloadEndTime = Date.now();
|
|
6519
|
+
this.logger.action(
|
|
6520
|
+
"memory-preload",
|
|
6521
|
+
`Preloaded ${Object.keys(memory.sessionMemory).length} session memory entries`,
|
|
6522
|
+
0,
|
|
6523
|
+
preloadStartTime,
|
|
6524
|
+
preloadEndTime,
|
|
6525
|
+
preloadEndTime - preloadStartTime
|
|
6526
|
+
);
|
|
6527
|
+
return memory;
|
|
6528
|
+
}
|
|
6529
|
+
return { sessionMemory: {}, history: [] };
|
|
6530
|
+
}
|
|
6500
6531
|
/**
|
|
6501
6532
|
* Reload tools from knowledge map state (cross-turn persistence)
|
|
6502
6533
|
*
|
|
@@ -6776,6 +6807,7 @@ var Agent = class {
|
|
|
6776
6807
|
const structuredOutput = await callLLMForAgentCompletion(adapter, {
|
|
6777
6808
|
systemPrompt,
|
|
6778
6809
|
memoryContext: this.memoryManager.toContext(this.iterationNumber, this.executionContext?.sessionTurnNumber),
|
|
6810
|
+
conversationHistory: this.executionContext?.conversationHistory,
|
|
6779
6811
|
outputSchema,
|
|
6780
6812
|
constraints: {
|
|
6781
6813
|
maxOutputTokens: this.modelConfig.maxOutputTokens,
|
|
@@ -10254,6 +10286,7 @@ function buildWorkerExecutionContext(params) {
|
|
|
10254
10286
|
resourceId: params.resourceId,
|
|
10255
10287
|
sessionId: params.sessionId,
|
|
10256
10288
|
sessionTurnNumber: params.sessionTurnNumber,
|
|
10289
|
+
conversationHistory: params.conversationHistory,
|
|
10257
10290
|
parentExecutionId: params.parentExecutionId,
|
|
10258
10291
|
executionDepth: params.executionDepth,
|
|
10259
10292
|
signal: params.signal,
|
|
@@ -10360,6 +10393,8 @@ function startWorker(org) {
|
|
|
10360
10393
|
organizationName,
|
|
10361
10394
|
sessionId,
|
|
10362
10395
|
sessionTurnNumber,
|
|
10396
|
+
sessionMemory,
|
|
10397
|
+
conversationHistory,
|
|
10363
10398
|
parentExecutionId,
|
|
10364
10399
|
executionDepth
|
|
10365
10400
|
} = msg;
|
|
@@ -10405,7 +10440,9 @@ function startWorker(org) {
|
|
|
10405
10440
|
try {
|
|
10406
10441
|
console.log(`[SDK-WORKER] Running agent '${resourceId}' (${agentDef.tools.length} tools)`);
|
|
10407
10442
|
const adapterFactory = createPostMessageAdapterFactory();
|
|
10408
|
-
const agentInstance = new Agent(agentDef, adapterFactory
|
|
10443
|
+
const agentInstance = new Agent(agentDef, adapterFactory, {
|
|
10444
|
+
initialMemory: sessionMemory
|
|
10445
|
+
});
|
|
10409
10446
|
const context = buildWorkerExecutionContext({
|
|
10410
10447
|
executionId,
|
|
10411
10448
|
organizationId: organizationId ?? "",
|
|
@@ -10413,6 +10450,7 @@ function startWorker(org) {
|
|
|
10413
10450
|
resourceId,
|
|
10414
10451
|
sessionId,
|
|
10415
10452
|
sessionTurnNumber,
|
|
10453
|
+
conversationHistory,
|
|
10416
10454
|
parentExecutionId,
|
|
10417
10455
|
executionDepth: executionDepth ?? 0,
|
|
10418
10456
|
signal: localAbortController.signal
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
* humanCheckpoints?: [...], relationships?: {...} }
|
|
12
12
|
*
|
|
13
13
|
* Parent -> Worker: { type: 'execute', resourceId, executionId, input, organizationId?, organizationName?,
|
|
14
|
-
* sessionId?, sessionTurnNumber?,
|
|
14
|
+
* sessionId?, sessionTurnNumber?, sessionMemory?, conversationHistory?,
|
|
15
|
+
* parentExecutionId?, executionDepth }
|
|
15
16
|
* Worker -> Parent: { type: 'result', status, output?, memorySnapshot?, error?, logs, metrics: { durationMs } }
|
|
16
17
|
*
|
|
17
18
|
* Parent -> Worker: { type: 'abort' } (graceful abort before terminate)
|
package/dist/worker/index.js
CHANGED
|
@@ -2890,6 +2890,8 @@ function buildReasoningRequest(iterationContext) {
|
|
|
2890
2890
|
iterationContext.iteration,
|
|
2891
2891
|
iterationContext.executionContext.sessionTurnNumber
|
|
2892
2892
|
),
|
|
2893
|
+
// A session agent gets its own conversation. Non-session executions have none.
|
|
2894
|
+
conversationHistory: iterationContext.executionContext.conversationHistory ?? [],
|
|
2893
2895
|
includeMessageAction: isSessionCapable,
|
|
2894
2896
|
includeNavigateKnowledge: hasKnowledgeMap,
|
|
2895
2897
|
includeMemoryOps
|
|
@@ -3306,12 +3308,16 @@ function validateTokenConfiguration(model, maxOutputTokens) {
|
|
|
3306
3308
|
);
|
|
3307
3309
|
}
|
|
3308
3310
|
}
|
|
3311
|
+
function buildAgentMessages(systemPrompt, memoryContext, conversationHistory = []) {
|
|
3312
|
+
return [
|
|
3313
|
+
{ role: "system", content: systemPrompt },
|
|
3314
|
+
...conversationHistory.map(({ role, content }) => ({ role, content })),
|
|
3315
|
+
{ role: "user", content: memoryContext }
|
|
3316
|
+
];
|
|
3317
|
+
}
|
|
3309
3318
|
async function callLLMForAgentIteration(adapter, request) {
|
|
3310
3319
|
validateTokenConfiguration(request.model, request.constraints.maxOutputTokens);
|
|
3311
|
-
const messages =
|
|
3312
|
-
{ role: "system", content: request.systemPrompt },
|
|
3313
|
-
{ role: "user", content: request.memoryContext }
|
|
3314
|
-
];
|
|
3320
|
+
const messages = buildAgentMessages(request.systemPrompt, request.memoryContext, request.conversationHistory);
|
|
3315
3321
|
const response = await adapter.generate({
|
|
3316
3322
|
messages,
|
|
3317
3323
|
responseSchema: buildIterationResponseSchema(
|
|
@@ -3340,10 +3346,7 @@ async function callLLMForAgentIteration(adapter, request) {
|
|
|
3340
3346
|
async function callLLMForAgentCompletion(adapter, request) {
|
|
3341
3347
|
validateTokenConfiguration(request.model, request.constraints.maxOutputTokens);
|
|
3342
3348
|
const response = await adapter.generate({
|
|
3343
|
-
messages:
|
|
3344
|
-
{ role: "system", content: request.systemPrompt },
|
|
3345
|
-
{ role: "user", content: request.memoryContext }
|
|
3346
|
-
],
|
|
3349
|
+
messages: buildAgentMessages(request.systemPrompt, request.memoryContext, request.conversationHistory),
|
|
3347
3350
|
responseSchema: request.outputSchema,
|
|
3348
3351
|
// Use output schema directly
|
|
3349
3352
|
temperature: request.constraints.temperature || 0.3,
|
|
@@ -3477,6 +3480,7 @@ async function processReasoning(iterationContext) {
|
|
|
3477
3480
|
const { reasoning, memoryOps, nextActions } = await callLLMForAgentIteration(adapter, {
|
|
3478
3481
|
systemPrompt: request.systemPrompt,
|
|
3479
3482
|
memoryContext: request.memoryContext,
|
|
3483
|
+
conversationHistory: request.conversationHistory,
|
|
3480
3484
|
tools: request.tools,
|
|
3481
3485
|
constraints: request.constraints,
|
|
3482
3486
|
model: iterationContext.modelConfig.model,
|
|
@@ -4349,11 +4353,10 @@ var MemoryManager = class {
|
|
|
4349
4353
|
*/
|
|
4350
4354
|
toContext(currentIteration, currentTurn) {
|
|
4351
4355
|
const status = this.getStatus();
|
|
4352
|
-
const
|
|
4353
|
-
|
|
4354
|
-
).reverse();
|
|
4356
|
+
const inTurnScope = (entry) => !currentTurn || entry.turnNumber === currentTurn || entry.turnNumber == null;
|
|
4357
|
+
const currentContext = this.memory.history.filter((entry) => inTurnScope(entry) && entry.iterationNumber === currentIteration).reverse();
|
|
4355
4358
|
const earlierContext = this.memory.history.filter(
|
|
4356
|
-
(entry) => (
|
|
4359
|
+
(entry) => inTurnScope(entry) && entry.iterationNumber !== null && entry.iterationNumber < currentIteration
|
|
4357
4360
|
);
|
|
4358
4361
|
const formatEntry = (entry) => {
|
|
4359
4362
|
const label = `[${entry.type.toUpperCase()}]`;
|
|
@@ -4430,6 +4433,9 @@ function initializeKnowledgeMap(knowledgeMap) {
|
|
|
4430
4433
|
nodes: Object.fromEntries(Object.entries(knowledgeMap.nodes).map(([id, node]) => [id, { ...node }]))
|
|
4431
4434
|
};
|
|
4432
4435
|
}
|
|
4436
|
+
function hasMemoryContent(memory) {
|
|
4437
|
+
return Object.keys(memory.sessionMemory).length > 0 || memory.history.length > 0;
|
|
4438
|
+
}
|
|
4433
4439
|
var Agent = class {
|
|
4434
4440
|
// Base properties from definition
|
|
4435
4441
|
config;
|
|
@@ -4439,6 +4445,7 @@ var Agent = class {
|
|
|
4439
4445
|
knowledgeMap;
|
|
4440
4446
|
definition;
|
|
4441
4447
|
adapterFactory;
|
|
4448
|
+
initialMemory;
|
|
4442
4449
|
// Derived properties (computed from definition)
|
|
4443
4450
|
shouldGenerateOutput;
|
|
4444
4451
|
// Runtime state (initialized during execution)
|
|
@@ -4453,10 +4460,12 @@ var Agent = class {
|
|
|
4453
4460
|
*
|
|
4454
4461
|
* @param definition - Agent definition with config, contract, tools, and optional preloadMemory
|
|
4455
4462
|
* @param adapterFactory - Factory for creating LLM adapters (decouples engine from provider SDKs)
|
|
4463
|
+
* @param options - Per-execution options (e.g. restored session memory)
|
|
4456
4464
|
*/
|
|
4457
|
-
constructor(definition, adapterFactory) {
|
|
4465
|
+
constructor(definition, adapterFactory, options = {}) {
|
|
4458
4466
|
this.definition = definition;
|
|
4459
4467
|
this.adapterFactory = adapterFactory;
|
|
4468
|
+
this.initialMemory = options.initialMemory;
|
|
4460
4469
|
this.config = definition.config;
|
|
4461
4470
|
this.contract = definition.contract;
|
|
4462
4471
|
this.modelConfig = definition.modelConfig;
|
|
@@ -4550,25 +4559,9 @@ var Agent = class {
|
|
|
4550
4559
|
* @returns Initialized MemoryManager instance
|
|
4551
4560
|
*/
|
|
4552
4561
|
async initializeMemoryManager(validatedInput, context) {
|
|
4553
|
-
|
|
4554
|
-
if (
|
|
4555
|
-
const preloadStartTime = Date.now();
|
|
4556
|
-
memory = await this.definition.preloadMemory(context);
|
|
4557
|
-
const preloadEndTime = Date.now();
|
|
4558
|
-
this.logger.action(
|
|
4559
|
-
"memory-preload",
|
|
4560
|
-
`Preloaded ${Object.keys(memory.sessionMemory).length} session memory entries`,
|
|
4561
|
-
0,
|
|
4562
|
-
preloadStartTime,
|
|
4563
|
-
preloadEndTime,
|
|
4564
|
-
preloadEndTime - preloadStartTime
|
|
4565
|
-
);
|
|
4562
|
+
const memory = await this.resolveInitialMemory(context);
|
|
4563
|
+
if (hasMemoryContent(memory)) {
|
|
4566
4564
|
await this.reloadKnowledgeMapTools(memory, context);
|
|
4567
|
-
} else {
|
|
4568
|
-
memory = {
|
|
4569
|
-
sessionMemory: {},
|
|
4570
|
-
history: []
|
|
4571
|
-
};
|
|
4572
4565
|
}
|
|
4573
4566
|
const inputStartTime = Date.now();
|
|
4574
4567
|
memory.history.push({
|
|
@@ -4589,6 +4582,44 @@ var Agent = class {
|
|
|
4589
4582
|
);
|
|
4590
4583
|
return new MemoryManager(memory, this.config.constraints, this.logger);
|
|
4591
4584
|
}
|
|
4585
|
+
/**
|
|
4586
|
+
* Resolve the memory this execution starts from.
|
|
4587
|
+
*
|
|
4588
|
+
* Precedence: caller-supplied `initialMemory` (session restore) > the definition's
|
|
4589
|
+
* `preloadMemory` author hook > empty. Returns a detached copy in the restore case so
|
|
4590
|
+
* the agent's mutations cannot corrupt the caller's snapshot.
|
|
4591
|
+
*/
|
|
4592
|
+
async resolveInitialMemory(context) {
|
|
4593
|
+
if (this.initialMemory) {
|
|
4594
|
+
const restoreStartTime = Date.now();
|
|
4595
|
+
const memory = structuredClone(this.initialMemory);
|
|
4596
|
+
const restoreEndTime = Date.now();
|
|
4597
|
+
this.logger.action(
|
|
4598
|
+
"memory-restore",
|
|
4599
|
+
`Restored ${Object.keys(memory.sessionMemory).length} session memory entries`,
|
|
4600
|
+
0,
|
|
4601
|
+
restoreStartTime,
|
|
4602
|
+
restoreEndTime,
|
|
4603
|
+
restoreEndTime - restoreStartTime
|
|
4604
|
+
);
|
|
4605
|
+
return memory;
|
|
4606
|
+
}
|
|
4607
|
+
if (this.definition.preloadMemory) {
|
|
4608
|
+
const preloadStartTime = Date.now();
|
|
4609
|
+
const memory = await this.definition.preloadMemory(context);
|
|
4610
|
+
const preloadEndTime = Date.now();
|
|
4611
|
+
this.logger.action(
|
|
4612
|
+
"memory-preload",
|
|
4613
|
+
`Preloaded ${Object.keys(memory.sessionMemory).length} session memory entries`,
|
|
4614
|
+
0,
|
|
4615
|
+
preloadStartTime,
|
|
4616
|
+
preloadEndTime,
|
|
4617
|
+
preloadEndTime - preloadStartTime
|
|
4618
|
+
);
|
|
4619
|
+
return memory;
|
|
4620
|
+
}
|
|
4621
|
+
return { sessionMemory: {}, history: [] };
|
|
4622
|
+
}
|
|
4592
4623
|
/**
|
|
4593
4624
|
* Reload tools from knowledge map state (cross-turn persistence)
|
|
4594
4625
|
*
|
|
@@ -4868,6 +4899,7 @@ var Agent = class {
|
|
|
4868
4899
|
const structuredOutput = await callLLMForAgentCompletion(adapter, {
|
|
4869
4900
|
systemPrompt,
|
|
4870
4901
|
memoryContext: this.memoryManager.toContext(this.iterationNumber, this.executionContext?.sessionTurnNumber),
|
|
4902
|
+
conversationHistory: this.executionContext?.conversationHistory,
|
|
4871
4903
|
outputSchema,
|
|
4872
4904
|
constraints: {
|
|
4873
4905
|
maxOutputTokens: this.modelConfig.maxOutputTokens,
|
|
@@ -6883,6 +6915,7 @@ function buildWorkerExecutionContext(params) {
|
|
|
6883
6915
|
resourceId: params.resourceId,
|
|
6884
6916
|
sessionId: params.sessionId,
|
|
6885
6917
|
sessionTurnNumber: params.sessionTurnNumber,
|
|
6918
|
+
conversationHistory: params.conversationHistory,
|
|
6886
6919
|
parentExecutionId: params.parentExecutionId,
|
|
6887
6920
|
executionDepth: params.executionDepth,
|
|
6888
6921
|
signal: params.signal,
|
|
@@ -6989,6 +7022,8 @@ function startWorker(org) {
|
|
|
6989
7022
|
organizationName,
|
|
6990
7023
|
sessionId,
|
|
6991
7024
|
sessionTurnNumber,
|
|
7025
|
+
sessionMemory,
|
|
7026
|
+
conversationHistory,
|
|
6992
7027
|
parentExecutionId,
|
|
6993
7028
|
executionDepth
|
|
6994
7029
|
} = msg;
|
|
@@ -7034,7 +7069,9 @@ function startWorker(org) {
|
|
|
7034
7069
|
try {
|
|
7035
7070
|
console.log(`[SDK-WORKER] Running agent '${resourceId}' (${agentDef.tools.length} tools)`);
|
|
7036
7071
|
const adapterFactory = createPostMessageAdapterFactory();
|
|
7037
|
-
const agentInstance = new Agent(agentDef, adapterFactory
|
|
7072
|
+
const agentInstance = new Agent(agentDef, adapterFactory, {
|
|
7073
|
+
initialMemory: sessionMemory
|
|
7074
|
+
});
|
|
7038
7075
|
const context = buildWorkerExecutionContext({
|
|
7039
7076
|
executionId,
|
|
7040
7077
|
organizationId: organizationId ?? "",
|
|
@@ -7042,6 +7079,7 @@ function startWorker(org) {
|
|
|
7042
7079
|
resourceId,
|
|
7043
7080
|
sessionId,
|
|
7044
7081
|
sessionTurnNumber,
|
|
7082
|
+
conversationHistory,
|
|
7045
7083
|
parentExecutionId,
|
|
7046
7084
|
executionDepth: executionDepth ?? 0,
|
|
7047
7085
|
signal: localAbortController.signal
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elevasis/sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.39.0",
|
|
4
4
|
"description": "SDK for building Elevasis organization resources",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -58,9 +58,9 @@
|
|
|
58
58
|
"tsup": "^8.0.0",
|
|
59
59
|
"typescript": "5.9.2",
|
|
60
60
|
"zod": "^4.1.0",
|
|
61
|
-
"@repo/
|
|
62
|
-
"@repo/
|
|
63
|
-
"@repo/
|
|
61
|
+
"@repo/core": "0.55.0",
|
|
62
|
+
"@repo/typescript-config": "0.0.0",
|
|
63
|
+
"@repo/eslint-config": "0.0.0"
|
|
64
64
|
},
|
|
65
65
|
"scripts": {
|
|
66
66
|
"lint": "eslint src --max-warnings 0",
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Session-capable agents now remember earlier turns
|
|
2
|
+
|
|
3
|
+
## Why this note exists
|
|
4
|
+
|
|
5
|
+
Any agent with `sessionCapable: true` was silently forgetting everything from earlier in the same
|
|
6
|
+
conversation. Two things were broken:
|
|
7
|
+
|
|
8
|
+
- **Its saved memory was never loaded back in.** The agent wrote memory at the end of a turn; the
|
|
9
|
+
next turn started from an empty memory instead of restoring it.
|
|
10
|
+
- **The earlier messages were never given to the model.** The conversation was stored for display
|
|
11
|
+
but the execution path never read it, so turn N could not see turns 1..N-1.
|
|
12
|
+
|
|
13
|
+
Both are now fixed, and both are **on by default** for session-capable agents. Turn N is handed the
|
|
14
|
+
agent's restored memory plus the earlier user/assistant messages (token-budgeted), so it can actually
|
|
15
|
+
continue the conversation. The fix is entirely platform-side — no agent definition changes are
|
|
16
|
+
required to get it.
|
|
17
|
+
|
|
18
|
+
## Applies to
|
|
19
|
+
|
|
20
|
+
- **Every agent with `sessionCapable: true`.** The behavior arrives with the `@elevasis/sdk` and
|
|
21
|
+
`@elevasis/core` dependency baselines this train propagates.
|
|
22
|
+
- **Especially agents whose memory strategy was written around the old broken behavior.** For
|
|
23
|
+
example, a voice/interview agent told _"never store the full transcript in memory — the transcript
|
|
24
|
+
is the source of truth"_ only worked if the transcript was actually replayed to the model. It
|
|
25
|
+
wasn't, so those agents deliberately declined to remember the one thing they needed. That guidance
|
|
26
|
+
is now correct: the earlier messages are given to the model.
|
|
27
|
+
|
|
28
|
+
## Required actions
|
|
29
|
+
|
|
30
|
+
1. **Take the `@elevasis/core` and `@elevasis/sdk` baseline bumps** this train propagates, then
|
|
31
|
+
reinstall in `operations/` so the new worker bundle is present.
|
|
32
|
+
2. **Redeploy your operations bundle.** The worker is baked into the deployed bundle at build time,
|
|
33
|
+
so an existing deployment stays amnesiac until it is redeployed. `pnpm -C operations exec
|
|
34
|
+
elevasis-sdk deploy --prod` (or your project's deploy command).
|
|
35
|
+
3. **Revisit any `memoryPreferences` you wrote around the old behavior.** If you told an agent not to
|
|
36
|
+
store something because "the conversation is the source of truth," confirm that still matches what
|
|
37
|
+
you want now that the conversation is actually available to the model.
|
|
38
|
+
|
|
39
|
+
## Verification
|
|
40
|
+
|
|
41
|
+
- Hard recall probe: plant an unguessable token in turn 1, then in turn 2 ask for it back verbatim
|
|
42
|
+
**without restating it**. A fixed agent returns it; a broken one says the session is empty.
|
|
43
|
+
- `turnInputTokens` grows turn over turn as the conversation accumulates, instead of staying flat at
|
|
44
|
+
system-prompt-plus-current-message size.
|
|
45
|
+
|
|
46
|
+
## Not handled by /git-sync
|
|
47
|
+
|
|
48
|
+
- **The redeploy.** `/git-sync` commits and pushes the propagated dependency baseline, but it does
|
|
49
|
+
not redeploy your operations bundle. The fix does not go live until you redeploy (action 2 above).
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# WorkOS single-org binding moves to the `.elevasis` marker
|
|
2
|
+
|
|
3
|
+
## Why this note exists
|
|
4
|
+
|
|
5
|
+
**This is a correctness fix for a live data-exposure class of bug. Read it before your next deploy.**
|
|
6
|
+
|
|
7
|
+
Single-org apps used to bind to their WorkOS organization through `VITE_WORKOS_ORG_ID`, a build-time env var. That variable lived only in a gitignored `ui/.env`, so it was absent from any clean build — CI, a fresh clone, a new hosting project. When it was absent the org guard in `__root.tsx` silently **no-opped**, and the app inherited whatever organization the WorkOS session happened to be using. It failed **open**.
|
|
8
|
+
|
|
9
|
+
That is not hypothetical. `app.contemplativerecords.com` served a different tenant's data because its production build had no `VITE_WORKOS_ORG_ID` set.
|
|
10
|
+
|
|
11
|
+
The binding now lives in the project's committed `.elevasis` marker:
|
|
12
|
+
|
|
13
|
+
```yaml
|
|
14
|
+
projectSlug: your-project
|
|
15
|
+
templateVersion: "1.0"
|
|
16
|
+
appMode: client-centric
|
|
17
|
+
workosOrgId: org_01ABCDEFGHIJKLMNOPQRSTUVWX
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The shared `elevasisVite()` plugin (from `@elevasis/ui/vite`, already wired into your `ui/vite.config.ts`) walks up from `ui/`, reads the marker, and injects the value as the build-time constant `__ELEVASIS_WORKOS_ORG_ID__`. The org guard, the `login.tsx` `signIn()` calls, and the dev-centric topbar switcher gate all read that constant.
|
|
21
|
+
|
|
22
|
+
**A WorkOS `org_` id is a public identifier, not a secret** — it appears in URLs. Committing it is correct. Treating it as a secret is what put it in a gitignored file and caused the failure.
|
|
23
|
+
|
|
24
|
+
`VITE_WORKOS_ORG_ID` is removed with **no fallback**. There is no transition period and no back-compat read. If you leave the env var set and do not seed the marker, your org guard stops binding.
|
|
25
|
+
|
|
26
|
+
## Applies to
|
|
27
|
+
|
|
28
|
+
- **Every template-derived project.** The plugin change arrives with the `@elevasis/ui` dependency baseline this train propagates.
|
|
29
|
+
- **`client-centric` projects — action required.** `/external verify` now fails closed: a `client-centric` project whose `.elevasis` lacks a non-empty `org_`-prefixed `workosOrgId` fails the gate. This is deliberate. A loud failure is the point; the old silent no-op is what shipped the bug.
|
|
30
|
+
- **`dev-centric` projects — optional.** The field may be absent or empty. If present and non-empty it must still be `org_`-prefixed.
|
|
31
|
+
|
|
32
|
+
## Required actions
|
|
33
|
+
|
|
34
|
+
1. **Add `workosOrgId` to your `.elevasis`.** Sync will not do this for you — see "Not handled by /git-sync" below. Find your org id in Command Center, or in the `organizations` table as `workos_org_id`.
|
|
35
|
+
2. **Take the `@elevasis/ui` baseline bump** this train propagates, then reinstall in `ui/` so the plugin that injects the constant is actually present.
|
|
36
|
+
3. **Remove `VITE_WORKOS_ORG_ID` from every environment you set it in** — `ui/.env`, `ui/.env.local`, and your hosting provider's env settings (for Vercel: Project → Settings → Environment Variables). Leaving it set does nothing, but it will mislead the next person who reads it.
|
|
37
|
+
4. **Redeploy.** The constant is injected at build time, so an existing deployment keeps its old behavior until it is rebuilt.
|
|
38
|
+
|
|
39
|
+
## Verification
|
|
40
|
+
|
|
41
|
+
- `pnpm external:verify` passes, and your project's `marker` category reports `.elevasis workosOrgId is set for client-centric project`.
|
|
42
|
+
- `grep -rn "VITE_WORKOS_ORG_ID" ui/ .env* 2>/dev/null` returns nothing.
|
|
43
|
+
- In a built bundle, your org id is present: `grep -o "org_[A-Za-z0-9]*" ui/dist/assets/*.js | head`. If this returns nothing for a `client-centric` project, the marker was not read — check that `.elevasis` sits at your project root, one level above `ui/`.
|
|
44
|
+
- After deploying, log in and confirm the app lands in the correct organization.
|
|
45
|
+
|
|
46
|
+
## Not handled by /git-sync
|
|
47
|
+
|
|
48
|
+
- **Your `.elevasis` marker is project-owned and `never-touch`.** The sync engine will never write it, which means it will never seed `workosOrgId` for you and never overwrite the value once you set it. Step 1 above is a manual, per-project edit. A `client-centric` project that skips it will fail `/external verify` until it is done.
|
|
49
|
+
- **A diverged `__root.tsx` will not auto-merge.** `__root.tsx` is merge-managed with `critical-manual-merge` severity. If your shell has diverged from the template, sync preserves your copy and the `useOrgGuard` swap from `import.meta.env.VITE_WORKOS_ORG_ID` to `__ELEVASIS_WORKOS_ORG_ID__` is a manual edit. When you make it, do **not** remove the structural contract substrings `ElevasisAuthenticatedShell`, `from '@elevasis/ui/app'`, or `SYSTEM_MANIFESTS` — dropping them makes `sync-apply` mis-escalate your project to `catch-up-required`.
|
|
50
|
+
- **A diverged `ui/vite.config.ts` needs reconciliation, not overwrite.** It is `replace-all` managed, so a blind sync would clobber local divergence. Confirm `...elevasisVite()` survives in your plugins array — without it the constant is never defined and the guard reads `undefined`.
|