@adonis-agora/agent 0.94.0 → 0.95.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.
Files changed (144) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/assets/spa/assets/{index-DUq09Q_k.js → index-DBu17cDY.js} +1 -1
  3. package/dist/assets/spa/index.html +1 -1
  4. package/dist/providers/poppy_connections_provider.d.ts +1 -1
  5. package/dist/providers/poppy_connections_provider.js +1 -1
  6. package/dist/providers/poppy_provider.d.ts +1 -1
  7. package/dist/providers/poppy_provider.d.ts.map +1 -1
  8. package/dist/providers/poppy_provider.js +6 -6
  9. package/dist/providers/poppy_provider.js.map +1 -1
  10. package/dist/src/index.d.ts +1 -1
  11. package/dist/src/index.js +1 -1
  12. package/dist/src/mcp/auth.d.ts +3 -3
  13. package/dist/src/mcp/auth.d.ts.map +1 -1
  14. package/dist/src/mcp/auth.js +2 -29
  15. package/dist/src/mcp/auth.js.map +1 -1
  16. package/dist/src/mcp/discovery.d.ts +3 -3
  17. package/dist/src/mcp/discovery.d.ts.map +1 -1
  18. package/dist/src/mcp/discovery.js +2 -2
  19. package/dist/src/poppy/auth.d.ts +20 -16
  20. package/dist/src/poppy/auth.d.ts.map +1 -1
  21. package/dist/src/poppy/auth.js.map +1 -1
  22. package/dist/src/poppy/conversations.d.ts +25 -25
  23. package/dist/src/poppy/conversations.d.ts.map +1 -1
  24. package/dist/src/poppy/conversations.js +37 -37
  25. package/dist/src/poppy/conversations.js.map +1 -1
  26. package/dist/src/poppy/define_config.d.ts +7 -7
  27. package/dist/src/poppy/define_config.d.ts.map +1 -1
  28. package/dist/src/poppy/define_config.js +1 -1
  29. package/dist/src/poppy/endpoints.d.ts +2 -2
  30. package/dist/src/poppy/endpoints.js +1 -1
  31. package/dist/src/poppy/handler.d.ts +5 -5
  32. package/dist/src/poppy/handler.d.ts.map +1 -1
  33. package/dist/src/poppy/handler.js +23 -21
  34. package/dist/src/poppy/handler.js.map +1 -1
  35. package/dist/src/poppy/index.d.ts +2 -2
  36. package/dist/src/poppy/index.d.ts.map +1 -1
  37. package/dist/src/poppy/index.js +1 -1
  38. package/dist/src/poppy/index.js.map +1 -1
  39. package/dist/src/poppy/protocol.d.ts +30 -23
  40. package/dist/src/poppy/protocol.d.ts.map +1 -1
  41. package/dist/src/poppy/protocol.js +16 -17
  42. package/dist/src/poppy/protocol.js.map +1 -1
  43. package/dist/src/poppy/redis-store.d.ts +1 -1
  44. package/dist/src/poppy/redis-store.js +1 -1
  45. package/dist/src/poppy/store.d.ts +7 -7
  46. package/dist/src/poppy/store.js +1 -1
  47. package/dist/src/poppy-client/api.d.ts +5 -4
  48. package/dist/src/poppy-client/api.d.ts.map +1 -1
  49. package/dist/src/poppy-client/api.js +14 -9
  50. package/dist/src/poppy-client/api.js.map +1 -1
  51. package/dist/src/poppy-client/browser.d.ts +12 -18
  52. package/dist/src/poppy-client/browser.d.ts.map +1 -1
  53. package/dist/src/poppy-client/browser.js +33 -73
  54. package/dist/src/poppy-client/browser.js.map +1 -1
  55. package/dist/src/poppy-client/cli/main.d.ts.map +1 -1
  56. package/dist/src/poppy-client/cli/main.js +48 -56
  57. package/dist/src/poppy-client/cli/main.js.map +1 -1
  58. package/dist/src/poppy-client/company.d.ts +43 -28
  59. package/dist/src/poppy-client/company.d.ts.map +1 -1
  60. package/dist/src/poppy-client/company.js +216 -160
  61. package/dist/src/poppy-client/company.js.map +1 -1
  62. package/dist/src/poppy-client/conversations.d.ts +12 -13
  63. package/dist/src/poppy-client/conversations.d.ts.map +1 -1
  64. package/dist/src/poppy-client/conversations.js +24 -22
  65. package/dist/src/poppy-client/conversations.js.map +1 -1
  66. package/dist/src/poppy-client/discovery.d.ts +32 -10
  67. package/dist/src/poppy-client/discovery.d.ts.map +1 -1
  68. package/dist/src/poppy-client/discovery.js +134 -68
  69. package/dist/src/poppy-client/discovery.js.map +1 -1
  70. package/dist/src/poppy-client/dpop.d.ts +2 -1
  71. package/dist/src/poppy-client/dpop.d.ts.map +1 -1
  72. package/dist/src/poppy-client/dpop.js +2 -1
  73. package/dist/src/poppy-client/dpop.js.map +1 -1
  74. package/dist/src/poppy-client/errors.d.ts +5 -4
  75. package/dist/src/poppy-client/errors.d.ts.map +1 -1
  76. package/dist/src/poppy-client/errors.js +5 -4
  77. package/dist/src/poppy-client/errors.js.map +1 -1
  78. package/dist/src/poppy-client/http.d.ts +4 -2
  79. package/dist/src/poppy-client/http.d.ts.map +1 -1
  80. package/dist/src/poppy-client/http.js +9 -5
  81. package/dist/src/poppy-client/http.js.map +1 -1
  82. package/dist/src/poppy-client/identity.d.ts +15 -15
  83. package/dist/src/poppy-client/identity.d.ts.map +1 -1
  84. package/dist/src/poppy-client/identity.js +17 -33
  85. package/dist/src/poppy-client/identity.js.map +1 -1
  86. package/dist/src/poppy-client/index.d.ts +6 -3
  87. package/dist/src/poppy-client/index.d.ts.map +1 -1
  88. package/dist/src/poppy-client/index.js +6 -3
  89. package/dist/src/poppy-client/index.js.map +1 -1
  90. package/dist/src/poppy-client/llm.js +4 -4
  91. package/dist/src/poppy-client/llm.js.map +1 -1
  92. package/dist/src/poppy-client/mcp.d.ts +3 -4
  93. package/dist/src/poppy-client/mcp.d.ts.map +1 -1
  94. package/dist/src/poppy-client/mcp.js +26 -38
  95. package/dist/src/poppy-client/mcp.js.map +1 -1
  96. package/dist/src/poppy-client/safe_fetch.d.ts +30 -0
  97. package/dist/src/poppy-client/safe_fetch.d.ts.map +1 -0
  98. package/dist/src/poppy-client/safe_fetch.js +210 -0
  99. package/dist/src/poppy-client/safe_fetch.js.map +1 -0
  100. package/dist/src/poppy-client/session_jwt.d.ts +20 -0
  101. package/dist/src/poppy-client/session_jwt.d.ts.map +1 -0
  102. package/dist/src/poppy-client/session_jwt.js +37 -0
  103. package/dist/src/poppy-client/session_jwt.js.map +1 -0
  104. package/dist/src/poppy-client/signin.d.ts +34 -11
  105. package/dist/src/poppy-client/signin.d.ts.map +1 -1
  106. package/dist/src/poppy-client/signin.js +134 -80
  107. package/dist/src/poppy-client/signin.js.map +1 -1
  108. package/dist/src/poppy-client/store.d.ts +23 -11
  109. package/dist/src/poppy-client/store.d.ts.map +1 -1
  110. package/dist/src/poppy-client/store.js +1 -1
  111. package/dist/src/poppy-client/store.js.map +1 -1
  112. package/dist/src/poppy-client/task_coverage.d.ts +13 -0
  113. package/dist/src/poppy-client/task_coverage.d.ts.map +1 -0
  114. package/dist/src/poppy-client/task_coverage.js +66 -0
  115. package/dist/src/poppy-client/task_coverage.js.map +1 -0
  116. package/dist/src/poppy-client/token_client.d.ts +36 -33
  117. package/dist/src/poppy-client/token_client.d.ts.map +1 -1
  118. package/dist/src/poppy-client/token_client.js +47 -47
  119. package/dist/src/poppy-client/token_client.js.map +1 -1
  120. package/dist/src/poppy-client/types.d.ts +81 -42
  121. package/dist/src/poppy-client/types.d.ts.map +1 -1
  122. package/dist/src/poppy-client/types.js +1 -1
  123. package/dist/src/poppy-client/util.d.ts +12 -3
  124. package/dist/src/poppy-client/util.d.ts.map +1 -1
  125. package/dist/src/poppy-client/util.js +29 -9
  126. package/dist/src/poppy-client/util.js.map +1 -1
  127. package/dist/src/poppy-connections/config.d.ts +2 -2
  128. package/dist/src/poppy-connections/config.js +1 -1
  129. package/dist/src/poppy-connections/config.js.map +1 -1
  130. package/dist/src/poppy-connections/crypto.d.ts +2 -2
  131. package/dist/src/poppy-connections/crypto.js +2 -2
  132. package/dist/src/poppy-connections/index.d.ts +1 -1
  133. package/dist/src/poppy-connections/index.js +1 -1
  134. package/dist/src/poppy-connections/service.d.ts +11 -10
  135. package/dist/src/poppy-connections/service.d.ts.map +1 -1
  136. package/dist/src/poppy-connections/service.js +33 -21
  137. package/dist/src/poppy-connections/service.js.map +1 -1
  138. package/dist/src/poppy-connections/store.d.ts +2 -2
  139. package/dist/src/poppy-connections/tools.d.ts.map +1 -1
  140. package/dist/src/poppy-connections/tools.js +5 -4
  141. package/dist/src/poppy-connections/tools.js.map +1 -1
  142. package/package.json +6 -6
  143. package/skills/agent-governance/SKILL.md +3 -3
  144. package/skills/agent-tools/SKILL.md +3 -3
@@ -112,7 +112,7 @@ export const POPPY_TABLES = {
112
112
  * `CREATE TABLE IF NOT EXISTS` DDL for the Poppy tables — the agent tables' portable dialect
113
113
  * (quoted identifiers, epoch-ms `BIGINT`, `TEXT` JSON; `forDialect` adapts it to MySQL). An event's
114
114
  * primary key is its position, so two appends racing for one position cannot both win; a message's
115
- * is its (owner, id), the idempotency lock of §7.3.
115
+ * is its (owner, id), the idempotency lock of §8.3.
116
116
  */
117
117
  export function poppyTableStatements() {
118
118
  const t = POPPY_TABLES;
@@ -1,6 +1,6 @@
1
1
  import type { CompanyClient } from './company.js';
2
2
  import type { PoppyApiEntry } from './types.js';
3
- /** API types this client understands (section 6). Others are skipped. */
3
+ /** API types this client understands (section 7). Others are skipped. */
4
4
  export declare const KNOWN_API_TYPES: string[];
5
5
  export declare function listApis(company: CompanyClient): PoppyApiEntry[];
6
6
  export declare function pickApi(company: CompanyClient, type: 'openapi' | 'mcp', index?: number | undefined): PoppyApiEntry;
@@ -14,13 +14,14 @@ export interface OpenApiDoc {
14
14
  }>>;
15
15
  [k: string]: unknown;
16
16
  }
17
- /** Loads an OpenAPI description. Tried without a token first; with DPoP if that's refused. */
17
+ /** Loads an OpenAPI description. Tried as a public document first; through the Session if that's refused. */
18
18
  export declare function loadOpenApi(company: CompanyClient, api: PoppyApiEntry): Promise<OpenApiDoc>;
19
19
  /** The API's base URL: `servers[0].url` resolved against the description's URL. */
20
20
  export declare function apiBaseUrl(doc: OpenApiDoc, api: PoppyApiEntry): string;
21
21
  /**
22
- * Calls an endpoint of a listed OpenAPI API with a DPoP Session Token (section 6). Refuses
23
- * absolute URLs outside the API's own origin, so tokens never go to a third party.
22
+ * Calls an endpoint of a listed OpenAPI API (section 7), through the Session and, when signed in,
23
+ * with an access token. Refuses absolute URLs outside the API's own origin, so nothing goes to a
24
+ * third party.
24
25
  */
25
26
  export declare function callOpenApi(company: CompanyClient, input: {
26
27
  method: string;
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../src/poppy-client/api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAIlD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAGhD,yEAAyE;AACzE,eAAO,MAAM,eAAe,UAAqB,CAAC;AAElD,wBAAgB,QAAQ,CAAC,OAAO,EAAE,aAAa,GAAG,aAAa,EAAE,CAIhE;AAED,wBAAgB,OAAO,CACrB,OAAO,EAAE,aAAa,EACtB,IAAI,EAAE,SAAS,GAAG,KAAK,EACvB,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,GACzB,aAAa,CAOf;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,EAAE,GAAG,SAAS,CAAC;IACxC,KAAK,CAAC,EAAE,MAAM,CACZ,MAAM,EACN,MAAM,CAAC,MAAM,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC,CACnF,CAAC;IACF,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACtB;AAED,8FAA8F;AAC9F,wBAAsB,WAAW,CAAC,OAAO,EAAE,aAAa,EAAE,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,UAAU,CAAC,CAWjG;AAED,mFAAmF;AACnF,wBAAgB,UAAU,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,aAAa,GAAG,MAAM,CAGtE;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,aAAa,EACtB,KAAK,EAAE;IACL,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5B,GACA,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC,CAiC9D"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../src/poppy-client/api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAIlD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAGhD,yEAAyE;AACzE,eAAO,MAAM,eAAe,UAAqB,CAAC;AAElD,wBAAgB,QAAQ,CAAC,OAAO,EAAE,aAAa,GAAG,aAAa,EAAE,CAIhE;AAED,wBAAgB,OAAO,CACrB,OAAO,EAAE,aAAa,EACtB,IAAI,EAAE,SAAS,GAAG,KAAK,EACvB,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,GACzB,aAAa,CAOf;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,EAAE,GAAG,SAAS,CAAC;IACxC,KAAK,CAAC,EAAE,MAAM,CACZ,MAAM,EACN,MAAM,CAAC,MAAM,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC,CACnF,CAAC;IACF,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACtB;AAED,6GAA6G;AAC7G,wBAAsB,WAAW,CAAC,OAAO,EAAE,aAAa,EAAE,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,UAAU,CAAC,CAWjG;AAED,mFAAmF;AACnF,wBAAgB,UAAU,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,aAAa,GAAG,MAAM,CAGtE;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,aAAa,EACtB,KAAK,EAAE;IACL,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5B,GACA,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC,CAqC9D"}
@@ -2,7 +2,7 @@ import { throwForResponse } from './company.js';
2
2
  import { PoppyError } from './errors.js';
3
3
  import { readJson } from './http.js';
4
4
  import { requireSecureUrl } from './util.js';
5
- /** API types this client understands (section 6). Others are skipped. */
5
+ /** API types this client understands (section 7). Others are skipped. */
6
6
  export const KNOWN_API_TYPES = ['openapi', 'mcp'];
7
7
  export function listApis(company) {
8
8
  return (company.document.apis ?? []).filter((a) => a && KNOWN_API_TYPES.includes(a.type) && typeof a.url === 'string');
@@ -17,7 +17,7 @@ export function pickApi(company, type, index) {
17
17
  requireSecureUrl(api.url, `${type} url`, company.security);
18
18
  return api;
19
19
  }
20
- /** Loads an OpenAPI description. Tried without a token first; with DPoP if that's refused. */
20
+ /** Loads an OpenAPI description. Tried as a public document first; through the Session if that's refused. */
21
21
  export async function loadOpenApi(company, api) {
22
22
  const res = await company.http.request(api.url, { headers: { accept: 'application/json' } });
23
23
  if (res.ok)
@@ -25,7 +25,7 @@ export async function loadOpenApi(company, api) {
25
25
  if (res.status === 401 || res.status === 403) {
26
26
  const { body } = await company.json(api.url, {
27
27
  headers: { accept: 'application/json' },
28
- resource: api.resource,
28
+ service: api.url,
29
29
  });
30
30
  return body ?? {};
31
31
  }
@@ -37,8 +37,9 @@ export function apiBaseUrl(doc, api) {
37
37
  return new URL(server ?? '/', api.url).toString().replace(/\/+$/, '');
38
38
  }
39
39
  /**
40
- * Calls an endpoint of a listed OpenAPI API with a DPoP Session Token (section 6). Refuses
41
- * absolute URLs outside the API's own origin, so tokens never go to a third party.
40
+ * Calls an endpoint of a listed OpenAPI API (section 7), through the Session and, when signed in,
41
+ * with an access token. Refuses absolute URLs outside the API's own origin, so nothing goes to a
42
+ * third party.
42
43
  */
43
44
  export async function callOpenApi(company, input) {
44
45
  const api = pickApi(company, 'openapi', input.apiIndex);
@@ -49,7 +50,7 @@ export async function callOpenApi(company, input) {
49
50
  : new URL(base + (input.path.startsWith('/') ? input.path : `/${input.path}`));
50
51
  const allowed = new Set([new URL(base).origin, new URL(api.url).origin]);
51
52
  if (!allowed.has(url.origin)) {
52
- throw new PoppyError('foreign_origin', `${url.origin} is not this API's origin; refusing to send the token`);
53
+ throw new PoppyError('foreign_origin', `${url.origin} is not this API's origin; refusing to send credentials`);
53
54
  }
54
55
  requireSecureUrl(url.toString(), 'API URL', company.security);
55
56
  const headers = { accept: 'application/json' };
@@ -59,11 +60,15 @@ export async function callOpenApi(company, input) {
59
60
  method: input.method.toUpperCase(),
60
61
  headers,
61
62
  body: input.body === undefined ? undefined : JSON.stringify(input.body),
62
- resource: api.resource,
63
+ service: base,
63
64
  scope: input.scope,
64
65
  });
65
- if (res.status === 401 || res.status === 403) {
66
- if (res.headers.get('www-authenticate'))
66
+ if ((res.status === 401 || res.status === 403) && res.headers.get('www-authenticate')) {
67
+ await throwForResponse(res);
68
+ }
69
+ if (res.status === 400) {
70
+ const refused = await readJson(res.clone());
71
+ if (refused?.error === 'invalid_session')
67
72
  await throwForResponse(res);
68
73
  }
69
74
  const text = await res.text();
@@ -1 +1 @@
1
- {"version":3,"file":"api.js","sourceRoot":"","sources":["../../../src/poppy-client/api.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../../src/poppy-client/api.ts"],"names":[],"mappings":"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"}
@@ -1,24 +1,18 @@
1
1
  import type { CompanyClient } from './company.js';
2
+ /** Where the Personal Agent's browser joins a Session (section 6), or an error without one. */
3
+ export declare function browserSessionEndpoint(company: CompanyClient): string;
2
4
  /**
3
- * Builds the browser assertion of section 5 for the current Session, after checking that
4
- * `return_to` is an HTTPS URL on `organization.domain` or a subdomain of it.
5
+ * Step 2: reads the one-time code from the HTML of the page the agent's OWN browser loaded in step
6
+ * 1 (section 6). Pass it nothing else: a code from anywhere else may come from someone else's
7
+ * browser, which would then join the User's Session (10.6). Keep the code out of model context.
5
8
  */
6
- export declare function createBrowserAssertion(company: CompanyClient, returnTo: string): Promise<{
7
- endpoint: string;
8
- assertion: string;
9
- sessionId: string;
10
- }>;
11
- /** A page whose form POSTs the assertion as a top-level navigation. Never in the URL (5). */
12
- export declare function autoSubmitFormHtml(endpoint: string, assertion: string): string;
9
+ export declare function browserJoinCode(html: string): string;
13
10
  /**
14
- * Serves the auto-submitting page once, on 127.0.0.1 at an unguessable path, so a local
15
- * browser can load it. The page is gone after the first load (the assertion lives 60s anyway).
11
+ * Step 2: authorizes the code with the Session header and, if signed in, the access token, which
12
+ * never reaches the browser. The agent's browser then submits the form (step 3); after a failure,
13
+ * start again at step 1.
16
14
  */
17
- export declare function serveOneShotPage(html: string, opts?: {
18
- timeoutMs?: number | undefined;
19
- }): Promise<{
20
- url: string;
21
- served: Promise<void>;
22
- close: () => void;
23
- }>;
15
+ export declare function authorizeBrowserJoin(company: CompanyClient, code: string, opts?: {
16
+ signedOut?: boolean | undefined;
17
+ }): Promise<void>;
24
18
  //# sourceMappingURL=browser.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../../src/poppy-client/browser.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAIlD;;;GAGG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CA8BrE;AAMD,6FAA6F;AAC7F,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAU9E;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAO,GAC5C,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAAC,KAAK,EAAE,MAAM,IAAI,CAAA;CAAE,CAAC,CAkCpE"}
1
+ {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../../src/poppy-client/browser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAKlD,+FAA+F;AAC/F,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CASrE;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQpD;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,aAAa,EACtB,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;CAAO,GAC7C,OAAO,CAAC,IAAI,CAAC,CAWf"}
@@ -1,83 +1,43 @@
1
- import { createServer } from 'node:http';
1
+ import { throwForResponse } from './company.js';
2
2
  import { PoppyError } from './errors.js';
3
- import { normalizeDomain, randomId, requireSecureUrl } from './util.js';
4
- /**
5
- * Builds the browser assertion of section 5 for the current Session, after checking that
6
- * `return_to` is an HTTPS URL on `organization.domain` or a subdomain of it.
7
- */
8
- export async function createBrowserAssertion(company, returnTo) {
9
- const ep = company.document.web?.browser_session_endpoint;
10
- if (!ep) {
3
+ import { requireSecureUrl } from './util.js';
4
+ /** Where the Personal Agent's browser joins a Session (section 6), or an error without one. */
5
+ export function browserSessionEndpoint(company) {
6
+ const endpoint = company.document.web?.browser_session_endpoint;
7
+ if (!endpoint) {
11
8
  throw new PoppyError('no_browser_session_endpoint', 'Company has no web.browser_session_endpoint: browse as an ordinary signed-out visitor');
12
9
  }
13
- const endpoint = requireSecureUrl(ep, 'web.browser_session_endpoint', company.security).toString();
14
- const target = requireSecureUrl(returnTo, 'return_to', company.security);
15
- const org = normalizeDomain(company.document.organization.domain.replace(/:\d+$/, ''));
16
- const host = target.hostname.toLowerCase();
17
- if (normalizeDomain(host) !== org && !host.endsWith(`.${org}`)) {
18
- throw new PoppyError('invalid_return_to', `return_to must be on ${org} or a subdomain (section 5)`);
19
- }
20
- const { sessionId } = await company.token();
21
- const assertion = await company.agent.identity.browserAssertion({
22
- userId: company.userId,
23
- endpoint,
24
- sessionId,
25
- returnTo: target.toString(),
26
- });
27
- return { endpoint, assertion, sessionId };
28
- }
29
- function escapeHtml(s) {
30
- return s.replace(/[&<>"']/g, (c) => `&#${c.charCodeAt(0)};`);
10
+ return requireSecureUrl(endpoint, 'web.browser_session_endpoint', company.security).toString();
31
11
  }
32
- /** A page whose form POSTs the assertion as a top-level navigation. Never in the URL (5). */
33
- export function autoSubmitFormHtml(endpoint, assertion) {
34
- return `<!doctype html>
35
- <html><head><meta charset="utf-8"><meta name="referrer" content="no-referrer"><title>Joining session…</title></head>
36
- <body>
37
- <form method="post" action="${escapeHtml(endpoint)}" enctype="application/x-www-form-urlencoded">
38
- <input type="hidden" name="assertion" value="${escapeHtml(assertion)}">
39
- <noscript><button type="submit">Continue</button></noscript>
40
- </form>
41
- <script>document.forms[0].submit()</script>
42
- </body></html>`;
12
+ /**
13
+ * Step 2: reads the one-time code from the HTML of the page the agent's OWN browser loaded in step
14
+ * 1 (section 6). Pass it nothing else: a code from anywhere else may come from someone else's
15
+ * browser, which would then join the User's Session (10.6). Keep the code out of model context.
16
+ */
17
+ export function browserJoinCode(html) {
18
+ for (const tag of html.match(/<input\b[^>]*>/gi) ?? []) {
19
+ if (!/\bname\s*=\s*["']?code["']?(?:\s|\/|>)/i.test(tag))
20
+ continue;
21
+ const value = /\bvalue\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/i.exec(tag);
22
+ const code = value?.[1] ?? value?.[2] ?? value?.[3];
23
+ if (code)
24
+ return code;
25
+ }
26
+ throw new PoppyError('invalid_browser_page', 'the browser-session page has no code field');
43
27
  }
44
28
  /**
45
- * Serves the auto-submitting page once, on 127.0.0.1 at an unguessable path, so a local
46
- * browser can load it. The page is gone after the first load (the assertion lives 60s anyway).
29
+ * Step 2: authorizes the code with the Session header and, if signed in, the access token, which
30
+ * never reaches the browser. The agent's browser then submits the form (step 3); after a failure,
31
+ * start again at step 1.
47
32
  */
48
- export async function serveOneShotPage(html, opts = {}) {
49
- const path = `/${randomId('', 16)}`;
50
- let resolveServed;
51
- const served = new Promise((r) => {
52
- resolveServed = r;
53
- });
54
- let used = false;
55
- const server = createServer((req, res) => {
56
- if (req.url !== path || used) {
57
- res.writeHead(404).end();
58
- return;
59
- }
60
- used = true;
61
- res.writeHead(200, {
62
- 'content-type': 'text/html; charset=utf-8',
63
- 'cache-control': 'no-store',
64
- 'referrer-policy': 'no-referrer',
65
- });
66
- res.end(html);
67
- resolveServed();
68
- setTimeout(() => server.close(), 1000).unref();
33
+ export async function authorizeBrowserJoin(company, code, opts = {}) {
34
+ const res = await company.fetch(`${browserSessionEndpoint(company).replace(/\/+$/, '')}/authorize`, {
35
+ method: 'POST',
36
+ headers: { 'content-type': 'application/json' },
37
+ body: JSON.stringify({ code }),
38
+ ...(opts.signedOut ? { signedOut: true } : {}),
69
39
  });
70
- await new Promise((r) => server.listen(0, '127.0.0.1', r));
71
- const timer = setTimeout(() => server.close(), opts.timeoutMs ?? 60_000);
72
- timer.unref();
73
- const { port } = server.address();
74
- return {
75
- url: `http://127.0.0.1:${port}${path}`,
76
- served,
77
- close: () => {
78
- clearTimeout(timer);
79
- server.close();
80
- },
81
- };
40
+ if (res.status !== 204)
41
+ await throwForResponse(res);
82
42
  }
83
43
  //# sourceMappingURL=browser.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"browser.js","sourceRoot":"","sources":["../../../src/poppy-client/browser.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"browser.js","sourceRoot":"","sources":["../../../src/poppy-client/browser.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAE7C,+FAA+F;AAC/F,MAAM,UAAU,sBAAsB,CAAC,OAAsB;IAC3D,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,GAAG,EAAE,wBAAwB,CAAC;IAChE,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,UAAU,CAClB,6BAA6B,EAC7B,uFAAuF,CACxF,CAAC;IACJ,CAAC;IACD,OAAO,gBAAgB,CAAC,QAAQ,EAAE,8BAA8B,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,QAAQ,EAAE,CAAC;AACjG,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,IAAI,EAAE,EAAE,CAAC;QACvD,IAAI,CAAC,yCAAyC,CAAC,IAAI,CAAC,GAAG,CAAC;YAAE,SAAS;QACnE,MAAM,KAAK,GAAG,kDAAkD,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC3E,MAAM,IAAI,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;QACpD,IAAI,IAAI;YAAE,OAAO,IAAI,CAAC;IACxB,CAAC;IACD,MAAM,IAAI,UAAU,CAAC,sBAAsB,EAAE,4CAA4C,CAAC,CAAC;AAC7F,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,OAAsB,EACtB,IAAY,EACZ,IAAI,GAAwC,EAAE;IAE9C,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,KAAK,CAC7B,GAAG,sBAAsB,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,YAAY,EAClE;QACE,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,CAAC;QAC9B,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC/C,CACF,CAAC;IACF,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG;QAAE,MAAM,gBAAgB,CAAC,GAAG,CAAC,CAAC;AACtD,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../../src/poppy-client/cli/main.ts"],"names":[],"mappings":"AAuHA,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAgE5D"}
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../../src/poppy-client/cli/main.ts"],"names":[],"mappings":"AAuHA,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA6D5D"}
@@ -1,6 +1,7 @@
1
+ import { readFile } from 'node:fs/promises';
1
2
  import { parseArgs } from 'node:util';
2
3
  import { callOpenApi, listApis, loadOpenApi } from '../api.js';
3
- import { autoSubmitFormHtml, createBrowserAssertion, serveOneShotPage } from '../browser.js';
4
+ import { authorizeBrowserJoin, browserJoinCode, browserSessionEndpoint } from '../browser.js';
4
5
  import { PoppyAgent } from '../company.js';
5
6
  import { ConversationClient, newMessageId, poppyProtocol } from '../conversations.js';
6
7
  import { discover } from '../discovery.js';
@@ -11,13 +12,13 @@ import { generateEs256Key } from '../keys.js';
11
12
  import { ask, DEFAULT_MODEL } from '../llm.js';
12
13
  import { connectMcp } from '../mcp.js';
13
14
  import { openInBrowser } from '../open_url.js';
14
- import { completeDirectSignIn, deviceSignIn, eligibleSignInTypes, mediatedSignIn, startDirectSignIn, submitMediatedCode, } from '../signin.js';
15
+ import { completeDirectSignIn, deviceSignIn, eligibleSignInTypes, mediatedExtension, mediatedSignIn, offeredSignInTypes, startDirectSignIn, submitMediatedCode, } from '../signin.js';
15
16
  import { StateStore } from '../store.js';
16
17
  import { isLoopbackHost, requireSecureUrl } from '../util.js';
17
18
  import { Prompter } from './prompt.js';
18
19
  const HELP = `poppy-agent — reference Personal Agent for the Personal Agent Protocol (Poppy)
19
20
 
20
- EXPERIMENTAL. Implements spec Draft 0.1 (https://personalagentprotocol.org/docs/spec).
21
+ EXPERIMENTAL. Implements spec Draft 0.3 (https://personalagentprotocol.org/docs/spec).
21
22
  The spec is still in development; this client tracks it and WILL change, possibly in
22
23
  breaking ways, as the spec evolves.
23
24
 
@@ -34,7 +35,7 @@ Sessions & sign-in (4)
34
35
  session <domain> [--new] [--scope <s>] [--resource <url>]
35
36
  signin <domain> [--method direct|device|mediated] [--scope "<scopes>"] [--no-open]
36
37
  status [domain]
37
- disconnect <domain> revoke the Account Token (RFC 7009)
38
+ disconnect <domain> revoke the refresh token (RFC 7009)
38
39
  APIs (6)
39
40
  apis <domain> list APIs (and OpenAPI operations)
40
41
  api <domain> <METHOD> <path> [--data <json>] [--api <n>] [--scope <s>]
@@ -43,7 +44,7 @@ APIs (6)
43
44
  Conversations (7)
44
45
  chat <domain> [--conversation <id>] [--as agent|human] [--no-stream]
45
46
  Web browsing (5)
46
- open <domain> <return_to> [--print] form-POST a poppy-browser+jwt assertion
47
+ open <domain> [--page <file>] authorize a browser join: code from that browser's page
47
48
  LLM mode
48
49
  ask <domain> "<task>" [--model <id>] [--max-steps <n>] [--yes]
49
50
  needs OPENROUTER_API_KEY (default model ${DEFAULT_MODEL})
@@ -51,7 +52,6 @@ LLM mode
51
52
  Global options
52
53
  --state <file> state file (default $POPPY_AGENT_STATE or ~/.config/poppy-agent/state.json), mode 0600
53
54
  --profile <name> local user to act for (default "default"); each gets its own User IDs
54
- --client-assertion-aud token_endpoint|issuer aud of private_key_jwt (default token_endpoint)
55
55
  --insecure-dev NON-SPEC: allow http:// URLs (client_id, discovery, endpoints). Local testing only.
56
56
  Also: POPPY_AGENT_INSECURE_DEV=1
57
57
  `;
@@ -72,14 +72,12 @@ function loadIdentity(ctx) {
72
72
  return new AgentIdentity(id, { insecureDev: ctx.insecureDev });
73
73
  }
74
74
  function makeAgent(ctx) {
75
- const aud = ctx.values['client-assertion-aud'];
76
75
  return new PoppyAgent({
77
76
  store: ctx.store,
78
77
  identity: loadIdentity(ctx),
79
78
  profile: ctx.profile,
80
79
  insecureDev: ctx.insecureDev,
81
80
  log: (l) => err(dim(`· ${l}`)),
82
- tokenClient: { clientAssertionAudience: aud === 'issuer' ? 'issuer' : 'token_endpoint' },
83
81
  });
84
82
  }
85
83
  async function company(ctx, domain) {
@@ -116,11 +114,10 @@ export async function runCli(argv) {
116
114
  conversation: { type: 'string' },
117
115
  as: { type: 'string' },
118
116
  'no-stream': { type: 'boolean' },
119
- print: { type: 'boolean' },
117
+ page: { type: 'string' },
120
118
  model: { type: 'string' },
121
119
  'max-steps': { type: 'string' },
122
120
  yes: { type: 'boolean' },
123
- 'client-assertion-aud': { type: 'string' },
124
121
  },
125
122
  });
126
123
  const [command, ...args] = positionals;
@@ -145,9 +142,6 @@ export async function runCli(argv) {
145
142
  if (e instanceof ResourceAuthError && e.code === 'insufficient_scope') {
146
143
  err(`error: insufficient_scope — the Company needs "${e.scope ?? '?'}". Run: poppy-agent signin <domain> --scope "${e.scope ?? ''}"`);
147
144
  }
148
- else if (e instanceof ResourceAuthError && e.code === 'sign_in_required') {
149
- err('error: sign_in_required — run: poppy-agent signin <domain>');
150
- }
151
145
  else {
152
146
  err(`error: ${e.message}`);
153
147
  }
@@ -183,12 +177,12 @@ async function dispatch(ctx, command, args) {
183
177
  const t = await c.token({ scope: str(v.scope), resource: str(v.resource) });
184
178
  out(JSON.stringify({
185
179
  user_id: c.userId,
186
- session_id: t.sessionId,
187
- signed_in: t.signedIn,
188
- scope: t.scope,
189
- token_type: t.tokenType,
190
- expires_at: new Date(t.expiresAt).toISOString(),
191
- account_token: c.accountToken
180
+ session_id: c.sessionId,
181
+ signed_in: t !== null,
182
+ scope: t?.scope ?? '',
183
+ token_type: t?.tokenType ?? null,
184
+ expires_at: t ? new Date(t.expiresAt).toISOString() : null,
185
+ refresh_token: c.accountToken
192
186
  ? { scope: c.accountToken.scope, via: c.accountToken.via }
193
187
  : null,
194
188
  }, null, 2));
@@ -205,14 +199,14 @@ async function dispatch(ctx, command, args) {
205
199
  const c = await company(ctx, args[0]);
206
200
  const revoked = await c.signOut();
207
201
  out(revoked
208
- ? 'Account Token revoked; Sessions continue signed out.'
209
- : 'No Account Token for this Company.');
202
+ ? 'Refresh token revoked; Sessions continue signed out.'
203
+ : 'No refresh token for this Company.');
210
204
  return 0;
211
205
  }
212
206
  case 'apis': {
213
207
  const c = await company(ctx, args[0]);
214
208
  for (const a of listApis(c)) {
215
- out(`${bold(`[${a.type}]`)} ${a.url}${a.resource ? ` (resource ${a.resource})` : ''} — ${a.description ?? ''}`);
209
+ out(`${bold(`[${a.type}]`)} ${a.url} — ${a.description ?? ''}`);
216
210
  if (a.type === 'openapi') {
217
211
  try {
218
212
  const doc = await loadOpenApi(c, a);
@@ -252,7 +246,7 @@ async function dispatch(ctx, command, args) {
252
246
  case 'chat':
253
247
  return cmdChat(ctx, args[0]);
254
248
  case 'open':
255
- return cmdOpen(ctx, args[0], args[1]);
249
+ return cmdOpen(ctx, args[0]);
256
250
  case 'ask':
257
251
  return cmdAsk(ctx, args[0], args.slice(1).join(' '));
258
252
  default:
@@ -320,14 +314,12 @@ async function cmdServeIdentity(ctx) {
320
314
  }
321
315
  /* --------------------------------------------------------------------------- sign-in */
322
316
  function defaultScope(c, method) {
323
- const offered = c.document.auth?.[method]?.scopes ?? [];
324
- const broad = offered.filter((s) => s === 'poppy:read' || s === 'poppy:write');
325
- return (broad.length ? broad : offered).join(' ');
317
+ if (method === 'mediated')
318
+ return (mediatedExtension(c)?.scopes ?? []).join(' ');
319
+ return (c.metadata.scopes_supported ?? Object.keys(c.document.auth?.scopes ?? {})).join(' ');
326
320
  }
327
321
  async function signIn(ctx, c, method, scope) {
328
- const eligible = scope
329
- ? eligibleSignInTypes(c, scope)
330
- : ['direct', 'device', 'mediated'].filter((t) => c.document.auth?.[t]);
322
+ const eligible = scope ? eligibleSignInTypes(c, scope) : offeredSignInTypes(c);
331
323
  const chosen = method ?? eligible[0];
332
324
  if (!chosen) {
333
325
  err(scope
@@ -343,7 +335,7 @@ async function signIn(ctx, c, method, scope) {
343
335
  else if (chosen === 'device') {
344
336
  result = await deviceSignIn(c, wanted, {
345
337
  display: (i) => {
346
- out(`Open ${bold(i.verificationUriComplete ?? i.verificationUri)} and enter the code ${bold(i.userCode)}`);
338
+ out(`${bold(i.organization.name)} (${i.organization.domain}): open ${bold(i.verificationUriComplete ?? i.verificationUri)} and enter the code ${bold(i.userCode)}`);
347
339
  out(dim(`(expires in ${i.expiresIn}s; waiting…)`));
348
340
  },
349
341
  onPoll: (s, n) => s === 'slow_down' && err(dim(`slow_down: polling every ${n}s`)),
@@ -352,7 +344,7 @@ async function signIn(ctx, c, method, scope) {
352
344
  else
353
345
  result = await mediatedFlow(ctx, c, wanted);
354
346
  if (result) {
355
- out(`Signed in. Granted scope: "${result.grantedScope}" (session ${result.sessionId})`);
347
+ out(`Signed in. Granted scope: "${result.grantedScope}" `);
356
348
  if (result.missingScopes.length)
357
349
  out(`Note: the User did not grant ${result.missingScopes.join(', ')}`);
358
350
  }
@@ -411,7 +403,7 @@ async function directFlow(ctx, c, scope) {
411
403
  }
412
404
  }
413
405
  async function mediatedFlow(ctx, c, scope) {
414
- const mediated = c.document.auth.mediated;
406
+ const mediated = mediatedExtension(c);
415
407
  const credentials = {};
416
408
  for (const f of mediated.fields) {
417
409
  credentials[f.name] = f.secret
@@ -422,7 +414,7 @@ async function mediatedFlow(ctx, c, scope) {
422
414
  for (const k of Object.keys(credentials))
423
415
  credentials[k] = '';
424
416
  while (outcome.status === 'code_required') {
425
- const code = await ctx.prompter.ask(`One-time code${outcome.sentTo ? ` (${outcome.sentTo})` : ''}${outcome.expiresAt ? `, before ${outcome.expiresAt}` : ''}: `);
417
+ const code = await ctx.prompter.ask(`One-time code${outcome.sentTo ? ` (${outcome.sentTo})` : ''}${outcome.expiresIn ? `, within ${outcome.expiresIn}s` : ''}: `);
426
418
  outcome = await submitMediatedCode(c, scope, outcome.signInId, code.trim());
427
419
  }
428
420
  if (outcome.status === 'complete')
@@ -436,12 +428,12 @@ async function cmdStatus(ctx, domain) {
436
428
  const id = ctx.store.data.identity;
437
429
  out(`identity: ${id ? `${id.baseUrl}/agent.json` : '(none: run init)'} profile: ${ctx.profile}`);
438
430
  for (const s of Object.values(companies)) {
439
- if (domain && !s.domains.includes(domain.toLowerCase().replace(/^www\./, '')))
431
+ if (domain && !s.domains.includes(domain.toLowerCase()))
440
432
  continue;
441
- const t = s.tokens.dpop;
433
+ const t = s.tokens.default;
442
434
  out(`${bold(s.issuer)} (${s.domains.join(', ')})`);
443
- out(` user_id ${s.userId} session ${s.sessionId ?? '-'} ${t ? `signed_in=${t.signedIn} scope="${t.scope}" expires ${new Date(t.expiresAt).toISOString()}` : ''}`);
444
- out(` account token: ${s.accountToken ? `yes (scope "${s.accountToken.scope}", via ${s.accountToken.via})` : 'no'}`);
435
+ out(` user_id ${s.userId} session ${s.sessionId ?? '-'} ${s.accountToken ? 'signed in' : 'signed out'}${t ? ` scope="${t.scope}" expires ${new Date(t.expiresAt).toISOString()}` : ''}`);
436
+ out(` refresh token: ${s.accountToken ? `yes (scope "${s.accountToken.scope}", via ${s.accountToken.via})` : 'no'}`);
445
437
  const convs = Object.entries(s.conversations).filter(([, c]) => !c.closed);
446
438
  if (convs.length)
447
439
  out(` open conversations: ${convs.map(([k]) => k).join(', ')}`);
@@ -474,19 +466,19 @@ async function cmdMcp(ctx, args) {
474
466
  }
475
467
  }
476
468
  /* --------------------------------------------------------------------------- open */
477
- async function cmdOpen(ctx, domain, returnTo) {
478
- if (!domain || !returnTo)
479
- throw new PoppyError('usage', 'open <domain> <return_to>');
469
+ async function cmdOpen(ctx, domain) {
470
+ if (!domain)
471
+ throw new PoppyError('usage', 'open <domain> [--page <file>]');
480
472
  const c = await company(ctx, domain);
481
- const { endpoint, assertion } = await createBrowserAssertion(c, returnTo);
482
- const page = await serveOneShotPage(autoSubmitFormHtml(endpoint, assertion));
483
- err(dim("Spec 5: browser assertions MUST be posted only from a browser the Personal Agent controls, never from the User's own browser. Use a dedicated/automation browser profile."));
484
- out(`Load this page (once, within 60s) in the agent-controlled browser:\n ${page.url}`);
485
- out(dim(`It form-POSTs the assertion to ${endpoint}, which sets the Session cookie and 303s to return_to.`));
486
- if (!ctx.values.print)
487
- openInBrowser(page.url);
488
- await Promise.race([page.served, new Promise((r) => setTimeout(r, 60_000))]);
489
- page.close();
473
+ const endpoint = browserSessionEndpoint(c);
474
+ err(dim("Section 6: join only a browser this agent controls, never the User's own, and take the code only from the page it loaded."));
475
+ out(`In the agent-controlled browser, load:\n ${endpoint}`);
476
+ const page = str(ctx.values.page);
477
+ const code = page
478
+ ? browserJoinCode(await readFile(page, 'utf8'))
479
+ : (await ctx.prompter.askSecret('Code from that page (the form field `code`): ')).trim();
480
+ await authorizeBrowserJoin(c, code);
481
+ out('Authorized. Now submit the form in that browser, within its 60 seconds.');
490
482
  return 0;
491
483
  }
492
484
  /* --------------------------------------------------------------------------- ask */
@@ -565,7 +557,7 @@ async function cmdChat(ctx, domain) {
565
557
  provisional.delete(m.id);
566
558
  if (m.role === 'user')
567
559
  return; // our own message, echoed back
568
- let line = `${tag}${bold(`company[${m.sender}]`)}: ${m.text ?? ''}`;
560
+ let line = `${tag}${bold(`company[${m.author?.name ?? m.author?.type}]`)}: ${m.text ?? ''}`;
569
561
  if (m.data)
570
562
  line += `\n${dim(JSON.stringify(m.data, null, 2))}`;
571
563
  print(line);
@@ -658,7 +650,7 @@ async function cmdChat(ctx, domain) {
658
650
  continue;
659
651
  try {
660
652
  if (!line.startsWith('/')) {
661
- await send({ sender: directId ? 'human' : sender, text: line });
653
+ await send({ author: { type: directId ? 'human' : sender }, text: line });
662
654
  continue;
663
655
  }
664
656
  const [cmd, ...rest] = line.split(' ');
@@ -672,7 +664,7 @@ async function cmdChat(ctx, domain) {
672
664
  out(dim(`now sending as ${sender}`));
673
665
  }
674
666
  else if (cmd === '/data') {
675
- await send({ sender: directId ? 'human' : sender, data: JSON.parse(restText) });
667
+ await send({ author: { type: directId ? 'human' : sender }, data: JSON.parse(restText) });
676
668
  }
677
669
  else if (cmd === '/context') {
678
670
  const context = {};
@@ -682,7 +674,7 @@ async function cmdChat(ctx, domain) {
682
674
  continue;
683
675
  context[k] = val === 'true' ? true : val === 'false' ? false : val;
684
676
  }
685
- await send({ sender: 'agent', context });
677
+ await send({ author: { type: 'agent' }, context });
686
678
  }
687
679
  else if (cmd === '/status') {
688
680
  if (!mainId)
@@ -709,7 +701,7 @@ async function cmdChat(ctx, domain) {
709
701
  const r = await signIn(ctx, c, m, m ? scope : [method, ...scopeParts].filter(Boolean).join(' ') || scope);
710
702
  if (r) {
711
703
  pendingAuth = undefined;
712
- out(dim('Continue the conversation; the new Session Token is used from now on.'));
704
+ out(dim('Continue the conversation; the new access token is used from now on.'));
713
705
  }
714
706
  }
715
707
  else if (cmd === '/direct') {
@@ -718,7 +710,7 @@ async function cmdChat(ctx, domain) {
718
710
  else {
719
711
  const message = {
720
712
  id: newMessageId(),
721
- sender: 'human',
713
+ author: { type: 'human' },
722
714
  text: restText || 'Hi, I am here.',
723
715
  context: { user_available: true },
724
716
  };
@@ -735,7 +727,7 @@ async function cmdChat(ctx, domain) {
735
727
  else {
736
728
  await conv.send(mainId, {
737
729
  id: newMessageId(),
738
- sender: 'agent',
730
+ author: { type: 'agent' },
739
731
  text: pendingUserRequest
740
732
  ? 'The user declined to talk directly right now.'
741
733
  : 'The user is not available.',