wave-agent-sdk 0.16.0 → 0.16.1

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 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/prompts/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAI/C,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAmBzD,eAAO,MAAM,kBAAkB,oKAAoK,CAAC;AAEpM,eAAO,MAAM,kBAAkB,opIAcqQ,CAAC;AAErS,eAAO,MAAM,wBAAwB,25DASqmB,CAAC;AAE3oB,eAAO,MAAM,WAAW,ihDAWqH,CAAC;AAE9I;;GAEG;AACH,eAAO,MAAM,wBAAwB,+uBAWiH,CAAC;AAEvJ,eAAO,MAAM,qBAAqB,6sBAMuL,CAAC;AAE1N,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,OAAO,EACnB,UAAU,GAAE,OAAe,GAC1B,MAAM,CAmFR;AAED,eAAO,MAAM,qBAAqB,oKAAqB,CAAC;AAExD,eAAO,MAAM,8BAA8B,44DA8CI,CAAC;AAEhD,eAAO,MAAM,yBAAyB,wHAAwH,CAAC;AAC/J,eAAO,MAAM,iBAAiB,qWAG4B,CAAC;AAE3D,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,KAAK,EAAE,UAAU,EAAE,EACnB,OAAO,GAAE;IACP,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE;QACT,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,OAAO,CAAC;KACrB,CAAC;IACF,UAAU,CAAC,EAAE;QACX,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,cAAc,CAAC,EAAE,cAAc,CAAC;CAC5B,GACL,MAAM,CAuER;AAED,wBAAgB,iCAAiC,CAC/C,oBAAoB,EAAE,MAAM,EAC5B,OAAO,EAAE,MAAM,GACd,MAAM,CAkCR"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/prompts/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAI/C,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAmBzD,eAAO,MAAM,kBAAkB,oKAAoK,CAAC;AAEpM,eAAO,MAAM,kBAAkB,opIAcqQ,CAAC;AAErS,eAAO,MAAM,wBAAwB,25DASqmB,CAAC;AAE3oB,eAAO,MAAM,WAAW,ihDAWqH,CAAC;AAE9I;;GAEG;AACH,eAAO,MAAM,wBAAwB,+uBAWiH,CAAC;AAEvJ,eAAO,MAAM,qBAAqB,6sBAMuL,CAAC;AAE1N,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,OAAO,EACnB,UAAU,GAAE,OAAe,GAC1B,MAAM,CAmFR;AAED,eAAO,MAAM,qBAAqB,oKAAqB,CAAC;AAExD,eAAO,MAAM,8BAA8B,44DA8CI,CAAC;AAEhD,eAAO,MAAM,yBAAyB,wHAAwH,CAAC;AAC/J,eAAO,MAAM,iBAAiB,qWAG4B,CAAC;AAE3D,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,KAAK,EAAE,UAAU,EAAE,EACnB,OAAO,GAAE;IACP,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE;QACT,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,OAAO,CAAC;KACrB,CAAC;IACF,UAAU,CAAC,EAAE;QACX,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,cAAc,CAAC,EAAE,cAAc,CAAC;CAC5B,GACL,MAAM,CA2ER;AAED,wBAAgB,iCAAiC,CAC/C,oBAAoB,EAAE,MAAM,EAC5B,OAAO,EAAE,MAAM,GACd,MAAM,CAkCR"}
@@ -207,11 +207,15 @@ export function buildSystemPrompt(basePrompt, tools, options = {}) {
207
207
  if (tools.length > 0) {
208
208
  prompt += `\n\n${TOOL_POLICY}`;
209
209
  }
210
- // List available deferred tool names so the model knows they exist
211
- // Matching Claude Code: deferred tools appear by name, not loaded until fetched.
212
- const deferredToolNames = tools.filter(isDeferredTool).map((t) => t.name);
213
- if (deferredToolNames.length > 0) {
214
- prompt += `\n\n<available-deferred-tools>${deferredToolNames.join(" ")}\nThese tools are NOT loaded yet — call ${TOOL_SEARCH_TOOL_NAME} first to discover their schemas before invoking them.</available-deferred-tools>`;
210
+ // List available deferred tool names with descriptions so the model knows they exist
211
+ // and can decide which ones to discover via ToolSearch
212
+ const deferredTools = tools.filter(isDeferredTool);
213
+ if (deferredTools.length > 0) {
214
+ const lines = deferredTools.map((t) => {
215
+ const desc = t.config.function?.description;
216
+ return desc ? `${t.name} - ${desc}` : t.name;
217
+ });
218
+ prompt += `\n\n<available-deferred-tools>\n${lines.join("\n")}\nThese tools are NOT loaded yet — call ${TOOL_SEARCH_TOOL_NAME} first to discover their schemas before invoking them.</available-deferred-tools>`;
215
219
  }
216
220
  prompt += `\n\n${OUTPUT_EFFICIENCY_PROMPT}`;
217
221
  prompt += `\n\n${TONE_AND_STYLE_PROMPT}`;
@@ -4,7 +4,7 @@
4
4
  * Handles SSO authentication via the admin server.
5
5
  * Manages auth token storage in ~/.wave/auth.json.
6
6
  */
7
- import type { AuthConfig } from "../types/auth.js";
7
+ import type { AuthConfig, AuthUser } from "../types/auth.js";
8
8
  export declare class AuthService {
9
9
  private static instance;
10
10
  static getInstance(): AuthService;
@@ -17,12 +17,18 @@ export declare class AuthService {
17
17
  login(options?: {
18
18
  /** Callback to receive the auth URL (for display in CLI). */
19
19
  onAuthUrl?: (url: string) => void;
20
- /** Read token manually (e.g. from stdin). Resolves with token or rejects on cancel. */
20
+ /** Read authorization code manually (e.g. from stdin). Resolves with code or rejects on cancel. */
21
21
  readToken?: () => Promise<string>;
22
22
  }): Promise<string>;
23
+ /**
24
+ * Exchange a short-lived authorization code for a JWT token.
25
+ * Returns both the token and user info.
26
+ */
27
+ private exchangeCode;
23
28
  private startLocalAuthServer;
24
29
  private openBrowser;
25
30
  isSSOAuthenticated(): boolean;
31
+ getAuthUser(): AuthUser | undefined;
26
32
  }
27
33
  export declare const authService: AuthService;
28
34
  //# sourceMappingURL=authService.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"authService.d.ts","sourceRoot":"","sources":["../../src/services/authService.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAgBH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAInD,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAc;IAErC,MAAM,CAAC,WAAW,IAAI,WAAW;IAOjC,WAAW,IAAI,MAAM;IAKrB,QAAQ,IAAI,UAAU;IAatB,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAUlC,SAAS,IAAI,IAAI;IAajB,WAAW,IAAI,MAAM,GAAG,SAAS;IAKjC,eAAe,IAAI,MAAM;IAUnB,KAAK,CAAC,OAAO,CAAC,EAAE;QACpB,6DAA6D;QAC7D,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAClC,uFAAuF;QACvF,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;KACnC,GAAG,OAAO,CAAC,MAAM,CAAC;IAgCnB,OAAO,CAAC,oBAAoB;YAiFd,WAAW;IAmBzB,kBAAkB,IAAI,OAAO;CAG9B;AAED,eAAO,MAAM,WAAW,aAA4B,CAAC"}
1
+ {"version":3,"file":"authService.d.ts","sourceRoot":"","sources":["../../src/services/authService.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAgBH,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAI7D,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAc;IAErC,MAAM,CAAC,WAAW,IAAI,WAAW;IAOjC,WAAW,IAAI,MAAM;IAKrB,QAAQ,IAAI,UAAU;IAatB,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAUlC,SAAS,IAAI,IAAI;IAajB,WAAW,IAAI,MAAM,GAAG,SAAS;IAKjC,eAAe,IAAI,MAAM;IAUnB,KAAK,CAAC,OAAO,CAAC,EAAE;QACpB,6DAA6D;QAC7D,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAClC,mGAAmG;QACnG,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;KACnC,GAAG,OAAO,CAAC,MAAM,CAAC;IAmBnB;;;OAGG;YACW,YAAY;IA0B1B,OAAO,CAAC,oBAAoB;YAoGd,WAAW;IAmBzB,kBAAkB,IAAI,OAAO;IAI7B,WAAW,IAAI,QAAQ,GAAG,SAAS;CAIpC;AAED,eAAO,MAAM,WAAW,aAA4B,CAAC"}
@@ -71,39 +71,68 @@ export class AuthService {
71
71
  }
72
72
  async login(options) {
73
73
  const adminUrl = this.getAdminBaseUrl();
74
- // Step 1: Fetch available SSO providers
75
- const providersResponse = await fetch(`${adminUrl}/api/auth/sso-providers`);
76
- if (!providersResponse.ok) {
77
- throw new Error(`Failed to fetch SSO providers: ${providersResponse.status} ${providersResponse.statusText}`);
78
- }
79
- const providers = (await providersResponse.json());
80
- if (!providers || providers.length === 0) {
81
- throw new Error("No SSO providers available");
82
- }
83
- const provider = providers[0].provider;
84
- // Step 2-5: Start local server, open browser, wait for callback or manual input
85
- const token = await this.startLocalAuthServer(adminUrl, provider, {
74
+ // Start local server, open browser, wait for callback or manual input
75
+ const { code } = await this.startLocalAuthServer(adminUrl, {
86
76
  onAuthUrl: options?.onAuthUrl,
87
77
  readToken: options?.readToken,
88
78
  });
89
- // Save the token (preserve existing keys)
79
+ // Exchange authorization code for JWT (includes user info)
80
+ const { token, user } = await this.exchangeCode(adminUrl, code);
81
+ // Save the token and user info (preserve existing keys)
90
82
  const existing = this.loadAuth();
91
- this.saveAuth({ ...existing, SSO_TOKEN: token });
83
+ this.saveAuth({ ...existing, SSO_TOKEN: token, user });
92
84
  return token;
93
85
  }
94
- startLocalAuthServer(adminUrl, provider, options) {
86
+ /**
87
+ * Exchange a short-lived authorization code for a JWT token.
88
+ * Returns both the token and user info.
89
+ */
90
+ async exchangeCode(adminUrl, code) {
91
+ const exchangeUrl = `${adminUrl}/api/auth/exchange`;
92
+ const response = await fetch(exchangeUrl, {
93
+ method: "POST",
94
+ headers: { "Content-Type": "application/json" },
95
+ body: JSON.stringify({ code }),
96
+ });
97
+ if (!response.ok) {
98
+ const text = await response.text();
99
+ throw new Error(`Token exchange failed (${response.status}): ${text}`);
100
+ }
101
+ const data = (await response.json());
102
+ return {
103
+ token: data.token,
104
+ user: { id: data.user.id, email: data.user.email },
105
+ };
106
+ }
107
+ startLocalAuthServer(adminUrl, options) {
95
108
  return new Promise((resolve, reject) => {
96
109
  let settled = false;
97
110
  const server = createServer((req, res) => {
98
111
  if (req.url) {
99
112
  const parsedUrl = new URL(req.url, `http://127.0.0.1`);
100
- const token = parsedUrl.searchParams.get("token");
113
+ const code = parsedUrl.searchParams.get("code");
114
+ const error = parsedUrl.searchParams.get("error");
115
+ if (error) {
116
+ res.writeHead(200, { "Content-Type": "text/html" });
117
+ res.end(`<html><body><h1>Authentication failed</h1><p>${parsedUrl.searchParams.get("error_description") || error}</p></body></html>`);
118
+ if (!settled) {
119
+ settled = true;
120
+ server.close();
121
+ reject(new Error(`SSO login failed: ${error}`));
122
+ }
123
+ return;
124
+ }
125
+ if (!code) {
126
+ res.writeHead(404, { "Content-Type": "text/html" });
127
+ res.end("<html><body><h1>Not Found</h1></body></html>");
128
+ return;
129
+ }
101
130
  res.writeHead(200, { "Content-Type": "text/html" });
102
131
  res.end("<html><body><h1>Authentication successful, you can close this window</h1></body></html>");
103
- if (token && !settled) {
132
+ if (!settled) {
104
133
  settled = true;
105
134
  server.close();
106
- resolve(token);
135
+ resolve({ code, user: { id: "", email: undefined } });
107
136
  }
108
137
  }
109
138
  });
@@ -117,7 +146,7 @@ export class AuthService {
117
146
  }
118
147
  const port = address.port;
119
148
  const callbackUrl = `http://127.0.0.1:${port}`;
120
- const authUrl = `${adminUrl}/api/auth/sso/${provider}?callback_url=${encodeURIComponent(callbackUrl)}`;
149
+ const authUrl = `${adminUrl}/login?callback_url=${encodeURIComponent(callbackUrl)}`;
121
150
  // Notify caller of the auth URL
122
151
  options?.onAuthUrl?.(authUrl);
123
152
  // Try to open browser; if it fails, keep server alive for manual visit
@@ -128,13 +157,13 @@ export class AuthService {
128
157
  // Browser not available — server stays alive
129
158
  }
130
159
  });
131
- // If manual token reading is provided, race between server callback and user input
160
+ // If manual code reading is provided, race between server callback and user input
132
161
  if (options?.readToken) {
133
- options.readToken().then((token) => {
162
+ options.readToken().then((code) => {
134
163
  if (!settled) {
135
164
  settled = true;
136
165
  server.close();
137
- resolve(token);
166
+ resolve({ code, user: { id: "", email: undefined } });
138
167
  }
139
168
  }, () => {
140
169
  // Manual input cancelled or closed, server keeps waiting for callback
@@ -171,5 +200,9 @@ export class AuthService {
171
200
  isSSOAuthenticated() {
172
201
  return this.getSSOToken() !== undefined;
173
202
  }
203
+ getAuthUser() {
204
+ const config = this.loadAuth();
205
+ return config.user;
206
+ }
174
207
  }
175
208
  export const authService = AuthService.getInstance();
@@ -1 +1 @@
1
- {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/telemetry/events.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAuB3D;;GAEG;AACH,wBAAsB,YAAY,CAChC,SAAS,EAAE,aAAa,EACxB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAC3C,OAAO,CAAC,IAAI,CAAC,CAoBf"}
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/telemetry/events.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AA2B3D;;GAEG;AACH,wBAAsB,YAAY,CAChC,SAAS,EAAE,aAAa,EACxB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAC3C,OAAO,CAAC,IAAI,CAAC,CAwBf"}
@@ -4,7 +4,7 @@
4
4
  * Provides the `logOTelEvent` API for structured session lifecycle events
5
5
  * with PII gates.
6
6
  */
7
- import { isInitialized, getCurrentConfig } from "./instrumentation.js";
7
+ import { isInitialized, getCurrentConfig, getTelemetryAttributes, } from "./instrumentation.js";
8
8
  let cachedLogger;
9
9
  async function getOTelLogger() {
10
10
  if (cachedLogger)
@@ -39,6 +39,10 @@ export async function logOTelEvent(eventName, metadata) {
39
39
  }
40
40
  log.emit({
41
41
  body: eventName,
42
- attributes: { "event.name": eventName, ...attributes },
42
+ attributes: {
43
+ "event.name": eventName,
44
+ ...getTelemetryAttributes(),
45
+ ...attributes,
46
+ },
43
47
  });
44
48
  }
@@ -62,4 +62,9 @@ export declare function getCurrentConfig(): TelemetryConfig | undefined;
62
62
  */
63
63
  export declare function isInitialized(): boolean;
64
64
  export { JsonlSpanExporter, JsonlLogExporter };
65
+ /**
66
+ * Get telemetry attributes based on the authenticated SSO user.
67
+ * Returns user.id and user.email when SSO authenticated, empty object otherwise.
68
+ */
69
+ export declare function getTelemetryAttributes(): Record<string, string>;
65
70
  //# sourceMappingURL=instrumentation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"instrumentation.d.ts","sourceRoot":"","sources":["../../src/telemetry/instrumentation.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,eAAe,EAGhB,MAAM,uBAAuB,CAAC;AAI/B,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAChF,OAAO,KAAK,EACV,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,yBAAyB,CAAC;AA0JjC;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,cAAc,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,GACxC,eAAe,CAmBjB;AAwCD;;GAEG;AACH,cAAM,iBAAkB,YAAW,YAAY;IAC7C,OAAO,CAAC,QAAQ,CAAS;gBAEb,QAAQ,CAAC,EAAE,MAAM;IAI7B,MAAM,CACJ,KAAK,EAAE,YAAY,EAAE,EACrB,cAAc,EAAE,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,GACjD,IAAI;IAwBP,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAIzB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;CAG5B;AAED;;GAEG;AACH,cAAM,gBAAiB,YAAW,iBAAiB;IACjD,OAAO,CAAC,QAAQ,CAAS;gBAEb,QAAQ,CAAC,EAAE,MAAM;IAI7B,MAAM,CACJ,IAAI,EAAE,iBAAiB,EAAE,EACzB,cAAc,EAAE,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,GACjD,IAAI;IAuBP,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAIzB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;CAG5B;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,GAChC,OAAO,CAAC,IAAI,CAAC,CA8Ef;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAuBvD;AAED;;;GAGG;AACH,wBAAgB,UAAU,IAAI,cAAc,oBAAoB,CAAC,GAAG,SAAS,CAE5E;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,eAAe,GAAG,SAAS,CAE9D;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,OAAO,CAEvC;AAGD,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,CAAC"}
1
+ {"version":3,"file":"instrumentation.d.ts","sourceRoot":"","sources":["../../src/telemetry/instrumentation.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,eAAe,EAGhB,MAAM,uBAAuB,CAAC;AAI/B,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAChF,OAAO,KAAK,EACV,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,yBAAyB,CAAC;AA2JjC;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,cAAc,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,GACxC,eAAe,CAmBjB;AAwCD;;GAEG;AACH,cAAM,iBAAkB,YAAW,YAAY;IAC7C,OAAO,CAAC,QAAQ,CAAS;gBAEb,QAAQ,CAAC,EAAE,MAAM;IAI7B,MAAM,CACJ,KAAK,EAAE,YAAY,EAAE,EACrB,cAAc,EAAE,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,GACjD,IAAI;IAwBP,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAIzB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;CAG5B;AAED;;GAEG;AACH,cAAM,gBAAiB,YAAW,iBAAiB;IACjD,OAAO,CAAC,QAAQ,CAAS;gBAEb,QAAQ,CAAC,EAAE,MAAM;IAI7B,MAAM,CACJ,IAAI,EAAE,iBAAiB,EAAE,EACzB,cAAc,EAAE,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,GACjD,IAAI;IAuBP,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAIzB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;CAG5B;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,GAChC,OAAO,CAAC,IAAI,CAAC,CA8Ef;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAuBvD;AAED;;;GAGG;AACH,wBAAgB,UAAU,IAAI,cAAc,oBAAoB,CAAC,GAAG,SAAS,CAE5E;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,eAAe,GAAG,SAAS,CAE9D;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,OAAO,CAEvC;AAGD,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,CAAC;AAE/C;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAc/D"}
@@ -6,6 +6,7 @@
6
6
  */
7
7
  import { logger } from "../utils/globalLogger.js";
8
8
  import * as fs from "node:fs";
9
+ import { AuthService } from "../services/authService.js";
9
10
  // Lazy-loaded OTEL modules — only imported when telemetry is initialized
10
11
  let sdkNode;
11
12
  let api;
@@ -369,3 +370,23 @@ export function isInitialized() {
369
370
  }
370
371
  // Export JSONL exporters for testing
371
372
  export { JsonlSpanExporter, JsonlLogExporter };
373
+ /**
374
+ * Get telemetry attributes based on the authenticated SSO user.
375
+ * Returns user.id and user.email when SSO authenticated, empty object otherwise.
376
+ */
377
+ export function getTelemetryAttributes() {
378
+ try {
379
+ const user = AuthService.getInstance().getAuthUser();
380
+ if (user) {
381
+ const attrs = { "user.id": user.id };
382
+ if (user.email) {
383
+ attrs["user.email"] = user.email;
384
+ }
385
+ return attrs;
386
+ }
387
+ }
388
+ catch {
389
+ // AuthService not available or not authenticated
390
+ }
391
+ return {};
392
+ }
@@ -103,7 +103,7 @@ export const toolSearchTool = {
103
103
  name: TOOL_SEARCH_TOOL_NAME,
104
104
  description: `Fetches full schema definitions for deferred tools so they can be called.
105
105
 
106
- Deferred tools appear by name in <available-deferred-tools> messages. Until fetched, only the name is knownthere is no parameter schema, so the tool cannot be invoked. This tool takes a query, matches it against the deferred tool list, and returns the matched tools' complete JSONSchema definitions inside a <functions> block. Once a tool's schema appears in that result, it is callable exactly like any tool defined at the top of the prompt.
106
+ Deferred tools appear by name and description in <available-deferred-tools> messages. The full parameter schema is NOT loaded yet use this tool to fetch it before invoking a deferred tool. This tool takes a query, matches it against the deferred tool list, and returns the matched tools' complete JSONSchema definitions inside a <functions> block. Once a tool's schema appears in that result, it is callable exactly like any tool defined at the top of the prompt.
107
107
 
108
108
  Result format: each matched tool appears as one <function>{"description": "...", "name": "...", "parameters": {...}}`,
109
109
  parameters: {
@@ -1,4 +1,9 @@
1
+ export interface AuthUser {
2
+ id: string;
3
+ email?: string;
4
+ }
1
5
  export interface AuthConfig {
2
6
  SSO_TOKEN?: string;
7
+ user?: AuthUser;
3
8
  }
4
9
  //# sourceMappingURL=auth.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/types/auth.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/types/auth.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,UAAU;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,QAAQ,CAAC;CACjB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wave-agent-sdk",
3
- "version": "0.16.0",
3
+ "version": "0.16.1",
4
4
  "description": "SDK for building AI-powered development tools and agents",
5
5
  "keywords": [
6
6
  "ai",
@@ -260,11 +260,15 @@ export function buildSystemPrompt(
260
260
  prompt += `\n\n${TOOL_POLICY}`;
261
261
  }
262
262
 
263
- // List available deferred tool names so the model knows they exist
264
- // Matching Claude Code: deferred tools appear by name, not loaded until fetched.
265
- const deferredToolNames = tools.filter(isDeferredTool).map((t) => t.name);
266
- if (deferredToolNames.length > 0) {
267
- prompt += `\n\n<available-deferred-tools>${deferredToolNames.join(" ")}\nThese tools are NOT loaded yet — call ${TOOL_SEARCH_TOOL_NAME} first to discover their schemas before invoking them.</available-deferred-tools>`;
263
+ // List available deferred tool names with descriptions so the model knows they exist
264
+ // and can decide which ones to discover via ToolSearch
265
+ const deferredTools = tools.filter(isDeferredTool);
266
+ if (deferredTools.length > 0) {
267
+ const lines = deferredTools.map((t) => {
268
+ const desc = t.config.function?.description;
269
+ return desc ? `${t.name} - ${desc}` : t.name;
270
+ });
271
+ prompt += `\n\n<available-deferred-tools>\n${lines.join("\n")}\nThese tools are NOT loaded yet — call ${TOOL_SEARCH_TOOL_NAME} first to discover their schemas before invoking them.</available-deferred-tools>`;
268
272
  }
269
273
 
270
274
  prompt += `\n\n${OUTPUT_EFFICIENCY_PROMPT}`;
@@ -19,7 +19,7 @@ import { createServer, Server } from "http";
19
19
  import { URL } from "url";
20
20
  import { execFile } from "child_process";
21
21
  import { promisify } from "util";
22
- import type { AuthConfig } from "../types/auth.js";
22
+ import type { AuthConfig, AuthUser } from "../types/auth.js";
23
23
 
24
24
  const execFileAsync = promisify(execFile);
25
25
 
@@ -92,64 +92,100 @@ export class AuthService {
92
92
  async login(options?: {
93
93
  /** Callback to receive the auth URL (for display in CLI). */
94
94
  onAuthUrl?: (url: string) => void;
95
- /** Read token manually (e.g. from stdin). Resolves with token or rejects on cancel. */
95
+ /** Read authorization code manually (e.g. from stdin). Resolves with code or rejects on cancel. */
96
96
  readToken?: () => Promise<string>;
97
97
  }): Promise<string> {
98
98
  const adminUrl = this.getAdminBaseUrl();
99
99
 
100
- // Step 1: Fetch available SSO providers
101
- const providersResponse = await fetch(`${adminUrl}/api/auth/sso-providers`);
102
- if (!providersResponse.ok) {
103
- throw new Error(
104
- `Failed to fetch SSO providers: ${providersResponse.status} ${providersResponse.statusText}`,
105
- );
106
- }
107
- const providers = (await providersResponse.json()) as {
108
- provider: string;
109
- displayName: string;
110
- }[];
111
- if (!providers || providers.length === 0) {
112
- throw new Error("No SSO providers available");
113
- }
114
- const provider = providers[0].provider;
115
-
116
- // Step 2-5: Start local server, open browser, wait for callback or manual input
117
- const token = await this.startLocalAuthServer(adminUrl, provider, {
100
+ // Start local server, open browser, wait for callback or manual input
101
+ const { code } = await this.startLocalAuthServer(adminUrl, {
118
102
  onAuthUrl: options?.onAuthUrl,
119
103
  readToken: options?.readToken,
120
104
  });
121
105
 
122
- // Save the token (preserve existing keys)
106
+ // Exchange authorization code for JWT (includes user info)
107
+ const { token, user } = await this.exchangeCode(adminUrl, code);
108
+
109
+ // Save the token and user info (preserve existing keys)
123
110
  const existing = this.loadAuth();
124
- this.saveAuth({ ...existing, SSO_TOKEN: token });
111
+ this.saveAuth({ ...existing, SSO_TOKEN: token, user });
125
112
 
126
113
  return token;
127
114
  }
128
115
 
116
+ /**
117
+ * Exchange a short-lived authorization code for a JWT token.
118
+ * Returns both the token and user info.
119
+ */
120
+ private async exchangeCode(
121
+ adminUrl: string,
122
+ code: string,
123
+ ): Promise<{ token: string; user: AuthUser }> {
124
+ const exchangeUrl = `${adminUrl}/api/auth/exchange`;
125
+ const response = await fetch(exchangeUrl, {
126
+ method: "POST",
127
+ headers: { "Content-Type": "application/json" },
128
+ body: JSON.stringify({ code }),
129
+ });
130
+
131
+ if (!response.ok) {
132
+ const text = await response.text();
133
+ throw new Error(`Token exchange failed (${response.status}): ${text}`);
134
+ }
135
+
136
+ const data = (await response.json()) as {
137
+ token: string;
138
+ user: { id: string; email?: string };
139
+ };
140
+ return {
141
+ token: data.token,
142
+ user: { id: data.user.id, email: data.user.email },
143
+ };
144
+ }
145
+
129
146
  private startLocalAuthServer(
130
147
  adminUrl: string,
131
- provider: string,
132
148
  options?: {
133
149
  onAuthUrl?: (url: string) => void;
134
150
  readToken?: () => Promise<string>;
135
151
  },
136
- ): Promise<string> {
152
+ ): Promise<{ code: string; user: AuthUser }> {
137
153
  return new Promise((resolve, reject) => {
138
154
  let settled = false;
139
155
  const server: Server = createServer((req, res) => {
140
156
  if (req.url) {
141
157
  const parsedUrl = new URL(req.url, `http://127.0.0.1`);
142
- const token = parsedUrl.searchParams.get("token");
158
+ const code = parsedUrl.searchParams.get("code");
159
+ const error = parsedUrl.searchParams.get("error");
160
+
161
+ if (error) {
162
+ res.writeHead(200, { "Content-Type": "text/html" });
163
+ res.end(
164
+ `<html><body><h1>Authentication failed</h1><p>${parsedUrl.searchParams.get("error_description") || error}</p></body></html>`,
165
+ );
166
+ if (!settled) {
167
+ settled = true;
168
+ server.close();
169
+ reject(new Error(`SSO login failed: ${error}`));
170
+ }
171
+ return;
172
+ }
173
+
174
+ if (!code) {
175
+ res.writeHead(404, { "Content-Type": "text/html" });
176
+ res.end("<html><body><h1>Not Found</h1></body></html>");
177
+ return;
178
+ }
143
179
 
144
180
  res.writeHead(200, { "Content-Type": "text/html" });
145
181
  res.end(
146
182
  "<html><body><h1>Authentication successful, you can close this window</h1></body></html>",
147
183
  );
148
184
 
149
- if (token && !settled) {
185
+ if (!settled) {
150
186
  settled = true;
151
187
  server.close();
152
- resolve(token);
188
+ resolve({ code, user: { id: "", email: undefined } });
153
189
  }
154
190
  }
155
191
  });
@@ -164,7 +200,7 @@ export class AuthService {
164
200
  }
165
201
  const port = address.port;
166
202
  const callbackUrl = `http://127.0.0.1:${port}`;
167
- const authUrl = `${adminUrl}/api/auth/sso/${provider}?callback_url=${encodeURIComponent(callbackUrl)}`;
203
+ const authUrl = `${adminUrl}/login?callback_url=${encodeURIComponent(callbackUrl)}`;
168
204
 
169
205
  // Notify caller of the auth URL
170
206
  options?.onAuthUrl?.(authUrl);
@@ -177,14 +213,14 @@ export class AuthService {
177
213
  }
178
214
  });
179
215
 
180
- // If manual token reading is provided, race between server callback and user input
216
+ // If manual code reading is provided, race between server callback and user input
181
217
  if (options?.readToken) {
182
218
  options.readToken().then(
183
- (token) => {
219
+ (code) => {
184
220
  if (!settled) {
185
221
  settled = true;
186
222
  server.close();
187
- resolve(token);
223
+ resolve({ code, user: { id: "", email: undefined } });
188
224
  }
189
225
  },
190
226
  () => {
@@ -229,6 +265,11 @@ export class AuthService {
229
265
  isSSOAuthenticated(): boolean {
230
266
  return this.getSSOToken() !== undefined;
231
267
  }
268
+
269
+ getAuthUser(): AuthUser | undefined {
270
+ const config = this.loadAuth();
271
+ return config.user;
272
+ }
232
273
  }
233
274
 
234
275
  export const authService = AuthService.getInstance();
@@ -6,7 +6,11 @@
6
6
  */
7
7
 
8
8
  import type { OTelEventName } from "../types/telemetry.js";
9
- import { isInitialized, getCurrentConfig } from "./instrumentation.js";
9
+ import {
10
+ isInitialized,
11
+ getCurrentConfig,
12
+ getTelemetryAttributes,
13
+ } from "./instrumentation.js";
10
14
 
11
15
  interface OTelLogger {
12
16
  emit: (logRecord: {
@@ -52,6 +56,10 @@ export async function logOTelEvent(
52
56
 
53
57
  log.emit({
54
58
  body: eventName,
55
- attributes: { "event.name": eventName, ...attributes },
59
+ attributes: {
60
+ "event.name": eventName,
61
+ ...getTelemetryAttributes(),
62
+ ...attributes,
63
+ },
56
64
  });
57
65
  }
@@ -18,6 +18,7 @@ import type {
18
18
  LogRecordExporter,
19
19
  ReadableLogRecord,
20
20
  } from "@opentelemetry/sdk-logs";
21
+ import { AuthService } from "../services/authService.js";
21
22
 
22
23
  // Lazy-loaded OTEL modules — only imported when telemetry is initialized
23
24
  let sdkNode: typeof import("@opentelemetry/sdk-node") | undefined;
@@ -468,3 +469,23 @@ export function isInitialized(): boolean {
468
469
 
469
470
  // Export JSONL exporters for testing
470
471
  export { JsonlSpanExporter, JsonlLogExporter };
472
+
473
+ /**
474
+ * Get telemetry attributes based on the authenticated SSO user.
475
+ * Returns user.id and user.email when SSO authenticated, empty object otherwise.
476
+ */
477
+ export function getTelemetryAttributes(): Record<string, string> {
478
+ try {
479
+ const user = AuthService.getInstance().getAuthUser();
480
+ if (user) {
481
+ const attrs: Record<string, string> = { "user.id": user.id };
482
+ if (user.email) {
483
+ attrs["user.email"] = user.email;
484
+ }
485
+ return attrs;
486
+ }
487
+ } catch {
488
+ // AuthService not available or not authenticated
489
+ }
490
+ return {};
491
+ }
@@ -127,7 +127,7 @@ export const toolSearchTool: ToolPlugin = {
127
127
  name: TOOL_SEARCH_TOOL_NAME,
128
128
  description: `Fetches full schema definitions for deferred tools so they can be called.
129
129
 
130
- Deferred tools appear by name in <available-deferred-tools> messages. Until fetched, only the name is knownthere is no parameter schema, so the tool cannot be invoked. This tool takes a query, matches it against the deferred tool list, and returns the matched tools' complete JSONSchema definitions inside a <functions> block. Once a tool's schema appears in that result, it is callable exactly like any tool defined at the top of the prompt.
130
+ Deferred tools appear by name and description in <available-deferred-tools> messages. The full parameter schema is NOT loaded yet use this tool to fetch it before invoking a deferred tool. This tool takes a query, matches it against the deferred tool list, and returns the matched tools' complete JSONSchema definitions inside a <functions> block. Once a tool's schema appears in that result, it is callable exactly like any tool defined at the top of the prompt.
131
131
 
132
132
  Result format: each matched tool appears as one <function>{"description": "...", "name": "...", "parameters": {...}}`,
133
133
  parameters: {
package/src/types/auth.ts CHANGED
@@ -1,3 +1,9 @@
1
+ export interface AuthUser {
2
+ id: string;
3
+ email?: string;
4
+ }
5
+
1
6
  export interface AuthConfig {
2
7
  SSO_TOKEN?: string;
8
+ user?: AuthUser;
3
9
  }