@getdial/cli 0.42.0 → 0.43.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.
@@ -1,6 +1,48 @@
1
1
  import { getCall } from "../../lib/ops/calls.js";
2
2
  import { isDialError } from "../../lib/ops/errors.js";
3
3
  import { printDialError } from "../../lib/cli-error.js";
4
+ /**
5
+ * A silence long enough to call out. Three seconds is past a natural beat between
6
+ * speakers but short enough to catch an agent thinking too long, which is what
7
+ * anyone reading a transcript for pacing is looking for.
8
+ */
9
+ const NOTABLE_PAUSE_MS = 3000;
10
+ /**
11
+ * Plain words for who spoke. "person" rather than "caller" because on an outbound
12
+ * call the human is the callee; "transferred" for the third party a call was
13
+ * handed off to, who is a different human from the one who was on the line first.
14
+ */
15
+ function speakerLabel(speaker) {
16
+ if (speaker === "agent")
17
+ return "agent";
18
+ return speaker === "transfer_target" ? "transferred" : "person";
19
+ }
20
+ /** ms into the call's audio → "m:ss.t", the stamp each transcript line carries. */
21
+ function formatOffset(ms) {
22
+ const minutes = Math.floor(ms / 60000);
23
+ const seconds = Math.floor((ms % 60000) / 1000);
24
+ const tenths = Math.floor((ms % 1000) / 100);
25
+ return `${minutes}:${String(seconds).padStart(2, "0")}.${tenths}`;
26
+ }
27
+ /**
28
+ * Print the transcript with each turn stamped, and call out the long silences
29
+ * between them. The flat string cannot show a pause, which is the whole reason
30
+ * the timed turns exist.
31
+ */
32
+ function printTimedTranscript(turns) {
33
+ console.log(`transcript:`);
34
+ turns.forEach((turn, i) => {
35
+ const previous = turns[i - 1];
36
+ if (previous) {
37
+ const pauseMs = turn.startMs - previous.endMs;
38
+ if (pauseMs >= NOTABLE_PAUSE_MS) {
39
+ console.log(` ... ${(pauseMs / 1000).toFixed(1)}s pause`);
40
+ }
41
+ }
42
+ const speaker = speakerLabel(turn.speaker).padEnd(12);
43
+ console.log(` ${formatOffset(turn.startMs).padStart(7)} ${speaker}${turn.text}`);
44
+ });
45
+ }
4
46
  export async function runCallGet(opts) {
5
47
  try {
6
48
  const c = await getCall(opts.callId);
@@ -19,7 +61,10 @@ export async function runCallGet(opts) {
19
61
  console.log(`instruction:`);
20
62
  console.log(c.instruction);
21
63
  }
22
- if (c.transcript) {
64
+ if (c.transcriptTurns?.length) {
65
+ printTimedTranscript(c.transcriptTurns);
66
+ }
67
+ else if (c.transcript) {
23
68
  console.log(`transcript:`);
24
69
  console.log(c.transcript);
25
70
  }
@@ -147,6 +147,17 @@ export const messageSchema = z
147
147
  createdAt: z.string().optional(),
148
148
  })
149
149
  .passthrough();
150
+ export const transcriptTurnSchema = z
151
+ .object({
152
+ speaker: z
153
+ .enum(["agent", "user", "transfer_target"])
154
+ .describe("`agent` is Dial's AI voice agent, `user` the human on the other end, and " +
155
+ "`transfer_target` the human the call was cold-transferred to"),
156
+ text: z.string().describe("What was said during the turn"),
157
+ startMs: z.number().describe("Approximate ms into the call's audio at which the turn began"),
158
+ endMs: z.number().describe("Approximate ms into the call's audio at which the turn ended"),
159
+ })
160
+ .describe("One uninterrupted stretch of speech by one party, placed in time");
150
161
  export const callSchema = z
151
162
  .object({
152
163
  id: z.string(),
@@ -156,6 +167,13 @@ export const callSchema = z
156
167
  status: statusSchema,
157
168
  duration: z.number().nullish(),
158
169
  transcript: z.string().nullish(),
170
+ transcriptTurns: transcriptTurnSchema
171
+ .array()
172
+ .nullish()
173
+ .describe("The same conversation as `transcript`, split into timed turns and ordered by " +
174
+ "startMs. Use it to measure pacing: the pause before a turn is its startMs minus " +
175
+ "the previous turn's endMs. Null when the call has no transcript, or when the " +
176
+ "call's turn timing was not recorded."),
159
177
  instruction: z.string().nullable().optional(),
160
178
  createdAt: z.string().optional(),
161
179
  })
@@ -9,7 +9,9 @@ export const getCallTool = {
9
9
  name: "get_call",
10
10
  config: {
11
11
  title: "Get Call",
12
- description: "Fetch a single call by id — status, duration, and transcript when available.",
12
+ description: "Fetch a single call by id — status, duration, and transcript when available. " +
13
+ "The transcript comes back twice: `transcript` as flat text, and `transcriptTurns` " +
14
+ "as timed turns for analysing pacing and spotting long pauses.",
13
15
  inputSchema,
14
16
  outputSchema: { call: callSchema },
15
17
  annotations: { readOnlyHint: true, openWorldHint: true },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getdial/cli",
3
- "version": "0.42.0",
3
+ "version": "0.43.0",
4
4
  "description": "Dial CLI — install, sign up, and run the local listen service.",
5
5
  "license": "MIT",
6
6
  "repository": {
package/skills.tar.gz CHANGED
Binary file