graphlit-client 1.0.20260419003 → 1.0.20260419004

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 CHANGED
@@ -3119,6 +3119,20 @@ 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;
3122
3136
  /**
3123
3137
  * Run LLM-as-judge quality assessment on a completed agent run.
3124
3138
  */
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 is a bare completion signal — it takes no arguments.
6985
- // The agent's user-facing answer MUST be in its final assistant message
6986
- // (the `fullMessage` / `finalAssistantMessage` on the turn result); this
6987
- // tool only marks the run as done.
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
- // Earlier versions had a `summary` field. That consistently caused the
6990
- // model to stuff its answer into the tool argument and then write a
6991
- // useless prose message ("answer delivered above") — the user would see
6992
- // the empty prose, not the answer. Removing the field eliminates the
6993
- // hiding place.
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. Your full answer must already be in your assistant message BEFORE calling this that prose is what the user sees. This tool takes no arguments; it only ends the run.",
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. Complete conversation (persist turn)
7258
- const trimmedMessage = loopResult.finalAssistantMessage;
7259
- if (trimmedMessage) {
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();
@@ -7271,7 +7311,7 @@ class Graphlit {
7271
7311
  if (loopResult.lastRoundReasoning) {
7272
7312
  const completionInput = {
7273
7313
  role: Types.ConversationRoleTypes.Assistant,
7274
- message: trimmedMessage,
7314
+ message: assistantMessageToPersist,
7275
7315
  timestamp: new Date().toISOString(),
7276
7316
  thinkingContent: loopResult.lastRoundReasoning.content,
7277
7317
  };
@@ -7284,7 +7324,7 @@ class Graphlit {
7284
7324
  completionInput,
7285
7325
  ];
7286
7326
  }
7287
- await this.completeConversation(trimmedMessage, conversationId, millisecondsToTimeSpan(completionTime), millisecondsToTimeSpan(ttft), throughput, undefined, turnMessageInputs, options?.correlationId);
7327
+ await this.completeConversation(assistantMessageToPersist, conversationId, millisecondsToTimeSpan(completionTime), millisecondsToTimeSpan(ttft), throughput, undefined, turnMessageInputs, options?.correlationId);
7288
7328
  // Emit completion event
7289
7329
  uiAdapter.handleEvent({
7290
7330
  type: "complete",
@@ -7293,12 +7333,11 @@ class Graphlit {
7293
7333
  }
7294
7334
  // 9. Build TurnResult
7295
7335
  const turnDuration = Date.now() - turnStart;
7296
- const taskCompleteThisTurn = this.detectTaskCompleteInMessages(loopResult.intermediateMessages);
7297
7336
  const turnToolNames = [...new Set(loopResult.toolCallNames)].sort();
7298
7337
  const turnResult = {
7299
7338
  turnNumber: turn,
7300
7339
  prompt: currentPrompt,
7301
- responseText: loopResult.fullMessage,
7340
+ responseText: taskCompleteFinalMessage || loopResult.fullMessage,
7302
7341
  toolCalls: turnToolNames,
7303
7342
  toolCallCount: loopResult.toolCallCount,
7304
7343
  durationMs: turnDuration,
@@ -7314,7 +7353,25 @@ class Graphlit {
7314
7353
  lastTurnUsage = undefined;
7315
7354
  turnResults.push(turnResult);
7316
7355
  totalToolCalls += loopResult.toolCallCount;
7317
- finalMessage = loopResult.finalAssistantMessage || loopResult.fullMessage;
7356
+ // Update finalMessage with priority:
7357
+ // - task_complete({final_message}) → authoritative
7358
+ // - task_complete() without arg → keep prior finalMessage (avoids
7359
+ // throwaway completion-turn prose overwriting a real answer)
7360
+ // - normal turn → this turn's assistant text
7361
+ if (taskCompleteThisTurn) {
7362
+ if (taskCompleteFinalMessage) {
7363
+ finalMessage = taskCompleteFinalMessage;
7364
+ }
7365
+ else if (!finalMessage) {
7366
+ // No prior answer — this turn's prose is all we have
7367
+ finalMessage =
7368
+ loopResult.finalAssistantMessage || loopResult.fullMessage;
7369
+ }
7370
+ }
7371
+ else {
7372
+ finalMessage =
7373
+ loopResult.finalAssistantMessage || loopResult.fullMessage;
7374
+ }
7318
7375
  lastContextWindow = loopResult.contextWindow;
7319
7376
  // 10. Notify callback
7320
7377
  options?.onTurnComplete?.(turnResult);
@@ -7564,6 +7621,56 @@ class Graphlit {
7564
7621
  }
7565
7622
  return false;
7566
7623
  }
7624
+ /**
7625
+ * Extract the `final_message` argument from a task_complete invocation
7626
+ * within a list of tool calls. Handles both direct invocation and
7627
+ * meta-executor wrapping (mirrors `detectTaskComplete`).
7628
+ *
7629
+ * Returns undefined if task_complete was not called, the arg is missing,
7630
+ * or the arg is not a non-empty string.
7631
+ */
7632
+ extractTaskCompleteFinalMessage(toolCalls) {
7633
+ if (!toolCalls)
7634
+ return undefined;
7635
+ for (const tc of toolCalls) {
7636
+ if (!tc?.arguments)
7637
+ continue;
7638
+ let args;
7639
+ try {
7640
+ args = JSON.parse(tc.arguments);
7641
+ }
7642
+ catch {
7643
+ continue;
7644
+ }
7645
+ // Direct invocation: args ARE the task_complete params
7646
+ if (tc.name === "task_complete") {
7647
+ const fm = args.final_message;
7648
+ if (typeof fm === "string" && fm.trim().length > 0)
7649
+ return fm;
7650
+ continue;
7651
+ }
7652
+ // Meta-executor wrapping: { tool: "task_complete", parameters: {...} }
7653
+ if (args.tool === "task_complete") {
7654
+ const params = args.parameters;
7655
+ const fm = params?.final_message;
7656
+ if (typeof fm === "string" && fm.trim().length > 0)
7657
+ return fm;
7658
+ }
7659
+ }
7660
+ return undefined;
7661
+ }
7662
+ /**
7663
+ * Scan a list of messages for a task_complete invocation's `final_message`
7664
+ * arg. Convenience wrapper over `extractTaskCompleteFinalMessage`.
7665
+ */
7666
+ extractTaskCompleteFinalMessageFromMessages(messages) {
7667
+ for (const msg of messages) {
7668
+ const fm = this.extractTaskCompleteFinalMessage(msg.toolCalls);
7669
+ if (fm)
7670
+ return fm;
7671
+ }
7672
+ return undefined;
7673
+ }
7567
7674
  /**
7568
7675
  * Run LLM-as-judge quality assessment on a completed agent run.
7569
7676
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "graphlit-client",
3
- "version": "1.0.20260419003",
3
+ "version": "1.0.20260419004",
4
4
  "description": "Graphlit API Client for TypeScript",
5
5
  "type": "module",
6
6
  "main": "./dist/client.js",