graphlit-client 1.0.20260419003 → 1.0.20260420001
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/client.d.ts +26 -0
- package/dist/client.js +186 -21
- package/package.json +1 -1
package/dist/client.d.ts
CHANGED
|
@@ -3119,6 +3119,32 @@ declare class Graphlit {
|
|
|
3119
3119
|
* with `intermediateMessages` rather than a single tool-call list.
|
|
3120
3120
|
*/
|
|
3121
3121
|
private detectTaskCompleteInMessages;
|
|
3122
|
+
/**
|
|
3123
|
+
* Extract the `final_message` argument from a task_complete invocation
|
|
3124
|
+
* within a list of tool calls. Handles both direct invocation and
|
|
3125
|
+
* meta-executor wrapping (mirrors `detectTaskComplete`).
|
|
3126
|
+
*
|
|
3127
|
+
* Returns undefined if task_complete was not called, the arg is missing,
|
|
3128
|
+
* or the arg is not a non-empty string.
|
|
3129
|
+
*/
|
|
3130
|
+
private extractTaskCompleteFinalMessage;
|
|
3131
|
+
/**
|
|
3132
|
+
* Scan a list of messages for a task_complete invocation's `final_message`
|
|
3133
|
+
* arg. Convenience wrapper over `extractTaskCompleteFinalMessage`.
|
|
3134
|
+
*/
|
|
3135
|
+
private extractTaskCompleteFinalMessageFromMessages;
|
|
3136
|
+
/**
|
|
3137
|
+
* Sanitize task_complete's `final_message` payload out of persisted
|
|
3138
|
+
* intermediate messages. The tool call record (name, id, timing, ordering)
|
|
3139
|
+
* is preserved as a completion marker, but its `final_message` arg is
|
|
3140
|
+
* stripped because that content is already stored as the turn's assistant
|
|
3141
|
+
* text via `completion`. Keeping the tool call without the payload yields
|
|
3142
|
+
* the same storage win as full removal while preserving the trace.
|
|
3143
|
+
*
|
|
3144
|
+
* Handles both direct and meta-executor (`execute_tool({tool: ...})`)
|
|
3145
|
+
* invocations, matching `detectTaskComplete`.
|
|
3146
|
+
*/
|
|
3147
|
+
private sanitizeTaskCompletePayload;
|
|
3122
3148
|
/**
|
|
3123
3149
|
* Run LLM-as-judge quality assessment on a completed agent run.
|
|
3124
3150
|
*/
|
package/dist/client.js
CHANGED
|
@@ -6981,22 +6981,32 @@ class Graphlit {
|
|
|
6981
6981
|
}
|
|
6982
6982
|
// Build tools list with task_complete prepended.
|
|
6983
6983
|
//
|
|
6984
|
-
// task_complete
|
|
6985
|
-
//
|
|
6986
|
-
//
|
|
6987
|
-
//
|
|
6984
|
+
// task_complete's optional `final_message` parameter is the explicit
|
|
6985
|
+
// commitment of the agent's user-facing answer. When provided, it
|
|
6986
|
+
// becomes the persisted assistant message for that turn AND the run's
|
|
6987
|
+
// `finalMessage`, overriding any prose the model wrote around the call.
|
|
6988
6988
|
//
|
|
6989
|
-
//
|
|
6990
|
-
//
|
|
6991
|
-
// useless prose
|
|
6992
|
-
//
|
|
6993
|
-
//
|
|
6989
|
+
// An earlier version used a `summary` field. The name encouraged
|
|
6990
|
+
// truncated summarization — the model would stuff a short summary into
|
|
6991
|
+
// the arg and emit useless prose like "answer delivered above". The
|
|
6992
|
+
// field name `final_message` and description below are explicit: this
|
|
6993
|
+
// IS the full answer, not a summary, and it replaces surrounding prose.
|
|
6994
|
+
//
|
|
6995
|
+
// If the model omits `final_message`, the SDK falls back to the last
|
|
6996
|
+
// turn whose assistant text was substantive AND did not itself call
|
|
6997
|
+
// task_complete — so a throwaway completion turn can't overwrite the
|
|
6998
|
+
// real answer in a prior turn.
|
|
6994
6999
|
const taskCompleteTool = {
|
|
6995
7000
|
name: "task_complete",
|
|
6996
|
-
description: "Signal that you have completed the assigned task.
|
|
7001
|
+
description: "Signal that you have completed the assigned task. Provide your complete, final user-facing answer as `final_message` — full prose, not a summary. That string becomes the assistant message the user sees; do not also emit the answer as plain text around this call. Calling this tool ends the run.",
|
|
6997
7002
|
schema: JSON.stringify({
|
|
6998
7003
|
type: "object",
|
|
6999
|
-
properties: {
|
|
7004
|
+
properties: {
|
|
7005
|
+
final_message: {
|
|
7006
|
+
type: "string",
|
|
7007
|
+
description: "Your complete, final answer to the user as full prose. Replaces any assistant text written around this tool call.",
|
|
7008
|
+
},
|
|
7009
|
+
},
|
|
7000
7010
|
}),
|
|
7001
7011
|
};
|
|
7002
7012
|
const allTools = [
|
|
@@ -7254,9 +7264,39 @@ class Graphlit {
|
|
|
7254
7264
|
finally {
|
|
7255
7265
|
uiAdapter.dispose();
|
|
7256
7266
|
}
|
|
7257
|
-
// 8.
|
|
7258
|
-
|
|
7259
|
-
|
|
7267
|
+
// 8. Detect task_complete and extract its optional `final_message`
|
|
7268
|
+
// arg BEFORE persistence. When provided, it replaces the turn's
|
|
7269
|
+
// assistant prose as the canonical answer — see the task_complete
|
|
7270
|
+
// tool schema above.
|
|
7271
|
+
const taskCompleteThisTurn = this.detectTaskCompleteInMessages(loopResult.intermediateMessages);
|
|
7272
|
+
const taskCompleteFinalMessage = taskCompleteThisTurn
|
|
7273
|
+
? this.extractTaskCompleteFinalMessageFromMessages(loopResult.intermediateMessages)
|
|
7274
|
+
: undefined;
|
|
7275
|
+
// Resolve the assistant message to persist for this turn:
|
|
7276
|
+
// - task_complete({final_message}) → final_message (authoritative)
|
|
7277
|
+
// - task_complete() with no arg, prior substantive answer exists →
|
|
7278
|
+
// drop the turn's prose so throwaway text like "delivered above"
|
|
7279
|
+
// doesn't pollute chat history
|
|
7280
|
+
// - task_complete() with no arg, no prior answer → keep the prose
|
|
7281
|
+
// (single-shot completion case)
|
|
7282
|
+
// - normal turn → keep the prose
|
|
7283
|
+
let assistantMessageToPersist;
|
|
7284
|
+
if (taskCompleteThisTurn) {
|
|
7285
|
+
if (taskCompleteFinalMessage) {
|
|
7286
|
+
assistantMessageToPersist = taskCompleteFinalMessage;
|
|
7287
|
+
}
|
|
7288
|
+
else if (finalMessage) {
|
|
7289
|
+
assistantMessageToPersist = "";
|
|
7290
|
+
}
|
|
7291
|
+
else {
|
|
7292
|
+
assistantMessageToPersist = loopResult.finalAssistantMessage;
|
|
7293
|
+
}
|
|
7294
|
+
}
|
|
7295
|
+
else {
|
|
7296
|
+
assistantMessageToPersist = loopResult.finalAssistantMessage;
|
|
7297
|
+
}
|
|
7298
|
+
// 8b. Complete conversation (persist turn)
|
|
7299
|
+
if (assistantMessageToPersist) {
|
|
7260
7300
|
const completionTime = uiAdapter.getCompletionTime();
|
|
7261
7301
|
const ttft = uiAdapter.getTTFT();
|
|
7262
7302
|
const throughput = uiAdapter.getThroughput();
|
|
@@ -7265,13 +7305,20 @@ class Graphlit {
|
|
|
7265
7305
|
return undefined;
|
|
7266
7306
|
return `PT${ms / 1000}S`;
|
|
7267
7307
|
};
|
|
7268
|
-
|
|
7269
|
-
|
|
7308
|
+
// Sanitize task_complete's final_message arg before persistence —
|
|
7309
|
+
// the content is already saved as the turn's assistant text, so
|
|
7310
|
+
// keeping it in the tool-call arguments would double-store it.
|
|
7311
|
+
// The tool call record itself stays as a completion marker.
|
|
7312
|
+
const sanitizedIntermediates = taskCompleteThisTurn
|
|
7313
|
+
? this.sanitizeTaskCompletePayload(loopResult.intermediateMessages)
|
|
7314
|
+
: loopResult.intermediateMessages;
|
|
7315
|
+
let turnMessageInputs = sanitizedIntermediates.length > 0
|
|
7316
|
+
? sanitizedIntermediates
|
|
7270
7317
|
: undefined;
|
|
7271
7318
|
if (loopResult.lastRoundReasoning) {
|
|
7272
7319
|
const completionInput = {
|
|
7273
7320
|
role: Types.ConversationRoleTypes.Assistant,
|
|
7274
|
-
message:
|
|
7321
|
+
message: assistantMessageToPersist,
|
|
7275
7322
|
timestamp: new Date().toISOString(),
|
|
7276
7323
|
thinkingContent: loopResult.lastRoundReasoning.content,
|
|
7277
7324
|
};
|
|
@@ -7284,7 +7331,7 @@ class Graphlit {
|
|
|
7284
7331
|
completionInput,
|
|
7285
7332
|
];
|
|
7286
7333
|
}
|
|
7287
|
-
await this.completeConversation(
|
|
7334
|
+
await this.completeConversation(assistantMessageToPersist, conversationId, millisecondsToTimeSpan(completionTime), millisecondsToTimeSpan(ttft), throughput, undefined, turnMessageInputs, options?.correlationId);
|
|
7288
7335
|
// Emit completion event
|
|
7289
7336
|
uiAdapter.handleEvent({
|
|
7290
7337
|
type: "complete",
|
|
@@ -7293,12 +7340,11 @@ class Graphlit {
|
|
|
7293
7340
|
}
|
|
7294
7341
|
// 9. Build TurnResult
|
|
7295
7342
|
const turnDuration = Date.now() - turnStart;
|
|
7296
|
-
const taskCompleteThisTurn = this.detectTaskCompleteInMessages(loopResult.intermediateMessages);
|
|
7297
7343
|
const turnToolNames = [...new Set(loopResult.toolCallNames)].sort();
|
|
7298
7344
|
const turnResult = {
|
|
7299
7345
|
turnNumber: turn,
|
|
7300
7346
|
prompt: currentPrompt,
|
|
7301
|
-
responseText: loopResult.fullMessage,
|
|
7347
|
+
responseText: taskCompleteFinalMessage || loopResult.fullMessage,
|
|
7302
7348
|
toolCalls: turnToolNames,
|
|
7303
7349
|
toolCallCount: loopResult.toolCallCount,
|
|
7304
7350
|
durationMs: turnDuration,
|
|
@@ -7314,7 +7360,25 @@ class Graphlit {
|
|
|
7314
7360
|
lastTurnUsage = undefined;
|
|
7315
7361
|
turnResults.push(turnResult);
|
|
7316
7362
|
totalToolCalls += loopResult.toolCallCount;
|
|
7317
|
-
|
|
7363
|
+
// Update finalMessage with priority:
|
|
7364
|
+
// - task_complete({final_message}) → authoritative
|
|
7365
|
+
// - task_complete() without arg → keep prior finalMessage (avoids
|
|
7366
|
+
// throwaway completion-turn prose overwriting a real answer)
|
|
7367
|
+
// - normal turn → this turn's assistant text
|
|
7368
|
+
if (taskCompleteThisTurn) {
|
|
7369
|
+
if (taskCompleteFinalMessage) {
|
|
7370
|
+
finalMessage = taskCompleteFinalMessage;
|
|
7371
|
+
}
|
|
7372
|
+
else if (!finalMessage) {
|
|
7373
|
+
// No prior answer — this turn's prose is all we have
|
|
7374
|
+
finalMessage =
|
|
7375
|
+
loopResult.finalAssistantMessage || loopResult.fullMessage;
|
|
7376
|
+
}
|
|
7377
|
+
}
|
|
7378
|
+
else {
|
|
7379
|
+
finalMessage =
|
|
7380
|
+
loopResult.finalAssistantMessage || loopResult.fullMessage;
|
|
7381
|
+
}
|
|
7318
7382
|
lastContextWindow = loopResult.contextWindow;
|
|
7319
7383
|
// 10. Notify callback
|
|
7320
7384
|
options?.onTurnComplete?.(turnResult);
|
|
@@ -7564,6 +7628,107 @@ class Graphlit {
|
|
|
7564
7628
|
}
|
|
7565
7629
|
return false;
|
|
7566
7630
|
}
|
|
7631
|
+
/**
|
|
7632
|
+
* Extract the `final_message` argument from a task_complete invocation
|
|
7633
|
+
* within a list of tool calls. Handles both direct invocation and
|
|
7634
|
+
* meta-executor wrapping (mirrors `detectTaskComplete`).
|
|
7635
|
+
*
|
|
7636
|
+
* Returns undefined if task_complete was not called, the arg is missing,
|
|
7637
|
+
* or the arg is not a non-empty string.
|
|
7638
|
+
*/
|
|
7639
|
+
extractTaskCompleteFinalMessage(toolCalls) {
|
|
7640
|
+
if (!toolCalls)
|
|
7641
|
+
return undefined;
|
|
7642
|
+
for (const tc of toolCalls) {
|
|
7643
|
+
if (!tc?.arguments)
|
|
7644
|
+
continue;
|
|
7645
|
+
let args;
|
|
7646
|
+
try {
|
|
7647
|
+
args = JSON.parse(tc.arguments);
|
|
7648
|
+
}
|
|
7649
|
+
catch {
|
|
7650
|
+
continue;
|
|
7651
|
+
}
|
|
7652
|
+
// Direct invocation: args ARE the task_complete params
|
|
7653
|
+
if (tc.name === "task_complete") {
|
|
7654
|
+
const fm = args.final_message;
|
|
7655
|
+
if (typeof fm === "string" && fm.trim().length > 0)
|
|
7656
|
+
return fm;
|
|
7657
|
+
continue;
|
|
7658
|
+
}
|
|
7659
|
+
// Meta-executor wrapping: { tool: "task_complete", parameters: {...} }
|
|
7660
|
+
if (args.tool === "task_complete") {
|
|
7661
|
+
const params = args.parameters;
|
|
7662
|
+
const fm = params?.final_message;
|
|
7663
|
+
if (typeof fm === "string" && fm.trim().length > 0)
|
|
7664
|
+
return fm;
|
|
7665
|
+
}
|
|
7666
|
+
}
|
|
7667
|
+
return undefined;
|
|
7668
|
+
}
|
|
7669
|
+
/**
|
|
7670
|
+
* Scan a list of messages for a task_complete invocation's `final_message`
|
|
7671
|
+
* arg. Convenience wrapper over `extractTaskCompleteFinalMessage`.
|
|
7672
|
+
*/
|
|
7673
|
+
extractTaskCompleteFinalMessageFromMessages(messages) {
|
|
7674
|
+
for (const msg of messages) {
|
|
7675
|
+
const fm = this.extractTaskCompleteFinalMessage(msg.toolCalls);
|
|
7676
|
+
if (fm)
|
|
7677
|
+
return fm;
|
|
7678
|
+
}
|
|
7679
|
+
return undefined;
|
|
7680
|
+
}
|
|
7681
|
+
/**
|
|
7682
|
+
* Sanitize task_complete's `final_message` payload out of persisted
|
|
7683
|
+
* intermediate messages. The tool call record (name, id, timing, ordering)
|
|
7684
|
+
* is preserved as a completion marker, but its `final_message` arg is
|
|
7685
|
+
* stripped because that content is already stored as the turn's assistant
|
|
7686
|
+
* text via `completion`. Keeping the tool call without the payload yields
|
|
7687
|
+
* the same storage win as full removal while preserving the trace.
|
|
7688
|
+
*
|
|
7689
|
+
* Handles both direct and meta-executor (`execute_tool({tool: ...})`)
|
|
7690
|
+
* invocations, matching `detectTaskComplete`.
|
|
7691
|
+
*/
|
|
7692
|
+
sanitizeTaskCompletePayload(messages) {
|
|
7693
|
+
return messages.map((msg) => {
|
|
7694
|
+
if (!msg.toolCalls || msg.toolCalls.length === 0)
|
|
7695
|
+
return msg;
|
|
7696
|
+
let mutated = false;
|
|
7697
|
+
const sanitizedToolCalls = msg.toolCalls.map((tc) => {
|
|
7698
|
+
if (!tc || !tc.arguments)
|
|
7699
|
+
return tc;
|
|
7700
|
+
let args;
|
|
7701
|
+
try {
|
|
7702
|
+
args = JSON.parse(tc.arguments);
|
|
7703
|
+
}
|
|
7704
|
+
catch {
|
|
7705
|
+
return tc;
|
|
7706
|
+
}
|
|
7707
|
+
// Direct invocation: args ARE the task_complete params
|
|
7708
|
+
if (tc.name === "task_complete" && "final_message" in args) {
|
|
7709
|
+
const { final_message: _dropped, ...rest } = args;
|
|
7710
|
+
void _dropped;
|
|
7711
|
+
mutated = true;
|
|
7712
|
+
return { ...tc, arguments: JSON.stringify(rest) };
|
|
7713
|
+
}
|
|
7714
|
+
// Meta-executor wrapping: { tool: "task_complete", parameters: {...} }
|
|
7715
|
+
if (args.tool === "task_complete") {
|
|
7716
|
+
const params = args.parameters;
|
|
7717
|
+
if (params && "final_message" in params) {
|
|
7718
|
+
const { final_message: _dropped, ...restParams } = params;
|
|
7719
|
+
void _dropped;
|
|
7720
|
+
mutated = true;
|
|
7721
|
+
return {
|
|
7722
|
+
...tc,
|
|
7723
|
+
arguments: JSON.stringify({ ...args, parameters: restParams }),
|
|
7724
|
+
};
|
|
7725
|
+
}
|
|
7726
|
+
}
|
|
7727
|
+
return tc;
|
|
7728
|
+
});
|
|
7729
|
+
return mutated ? { ...msg, toolCalls: sanitizedToolCalls } : msg;
|
|
7730
|
+
});
|
|
7731
|
+
}
|
|
7567
7732
|
/**
|
|
7568
7733
|
* Run LLM-as-judge quality assessment on a completed agent run.
|
|
7569
7734
|
*/
|