@hanzo/build 0.2.10 → 0.2.12

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 (136) hide show
  1. package/README.md +98 -25
  2. package/lib/api/agents.d.ts +80 -0
  3. package/lib/api/agents.js +148 -0
  4. package/lib/api/billing.d.ts +205 -0
  5. package/lib/api/billing.js +300 -0
  6. package/lib/api/call.d.ts +6 -0
  7. package/lib/api/call.js +11 -0
  8. package/lib/api/capabilities.d.ts +25 -0
  9. package/lib/api/capabilities.js +40 -0
  10. package/lib/api/coding.d.ts +31 -1
  11. package/lib/api/coding.js +50 -1
  12. package/lib/api/connectors.d.ts +90 -0
  13. package/lib/api/connectors.js +109 -0
  14. package/lib/api/consent.d.ts +22 -0
  15. package/lib/api/consent.js +28 -0
  16. package/lib/api/github.d.ts +13 -0
  17. package/lib/api/github.js +9 -0
  18. package/lib/api/harness.d.ts +44 -0
  19. package/lib/api/harness.js +348 -0
  20. package/lib/api/keys.d.ts +41 -0
  21. package/lib/api/keys.js +55 -0
  22. package/lib/api/machines.d.ts +56 -0
  23. package/lib/api/machines.js +84 -0
  24. package/lib/api/members.d.ts +47 -0
  25. package/lib/api/members.js +88 -0
  26. package/lib/api/memory.d.ts +25 -0
  27. package/lib/api/memory.js +34 -0
  28. package/lib/api/places.d.ts +2 -2
  29. package/lib/api/places.js +3 -26
  30. package/lib/api/plugins.d.ts +55 -0
  31. package/lib/api/plugins.js +62 -0
  32. package/lib/api/pref.d.ts +49 -0
  33. package/lib/api/pref.js +68 -0
  34. package/lib/api/profile.d.ts +21 -0
  35. package/lib/api/profile.js +40 -0
  36. package/lib/api/projects.d.ts +19 -2
  37. package/lib/api/projects.js +29 -4
  38. package/lib/api/provider.d.ts +42 -0
  39. package/lib/api/provider.js +58 -0
  40. package/lib/api/sessions.d.ts +32 -4
  41. package/lib/api/sessions.js +42 -6
  42. package/lib/api/skills.d.ts +70 -0
  43. package/lib/api/skills.js +90 -0
  44. package/lib/api/tools.d.ts +34 -0
  45. package/lib/api/tools.js +37 -0
  46. package/lib/api/turn.d.ts +84 -16
  47. package/lib/api/turn.js +221 -4
  48. package/lib/api/webhooks.d.ts +60 -0
  49. package/lib/api/webhooks.js +81 -0
  50. package/lib/ask.d.ts +18 -0
  51. package/lib/ask.js +69 -0
  52. package/lib/builder.js +39 -19
  53. package/lib/customize/agents.d.ts +2 -0
  54. package/lib/customize/agents.js +125 -0
  55. package/lib/customize/connectors.d.ts +2 -0
  56. package/lib/customize/connectors.js +199 -0
  57. package/lib/customize/index.d.ts +6 -0
  58. package/lib/customize/index.js +52 -0
  59. package/lib/customize/plugins.d.ts +2 -0
  60. package/lib/customize/plugins.js +93 -0
  61. package/lib/customize/skills.d.ts +2 -0
  62. package/lib/customize/skills.js +127 -0
  63. package/lib/customize/ui.d.ts +110 -0
  64. package/lib/customize/ui.js +99 -0
  65. package/lib/desk.d.ts +4 -1
  66. package/lib/desk.js +3 -16
  67. package/lib/find.d.ts +7 -0
  68. package/lib/find.js +77 -0
  69. package/lib/foot.d.ts +16 -0
  70. package/lib/foot.js +36 -0
  71. package/lib/forge.js +4 -4
  72. package/lib/git.d.ts +4 -0
  73. package/lib/git.js +6 -2
  74. package/lib/host.d.ts +5 -0
  75. package/lib/landing.d.ts +10 -0
  76. package/lib/landing.js +30 -14
  77. package/lib/markdown.d.ts +49 -0
  78. package/lib/markdown.js +123 -0
  79. package/lib/plans.d.ts +1 -0
  80. package/lib/plans.js +113 -0
  81. package/lib/prefs.d.ts +30 -0
  82. package/lib/prefs.js +83 -0
  83. package/lib/prose.d.ts +4 -0
  84. package/lib/prose.js +53 -0
  85. package/lib/route.d.ts +16 -1
  86. package/lib/route.js +32 -1
  87. package/lib/run.js +184 -32
  88. package/lib/section.js +2 -2
  89. package/lib/settings/account.d.ts +5 -0
  90. package/lib/settings/account.js +111 -0
  91. package/lib/settings/billing.d.ts +1 -0
  92. package/lib/settings/billing.js +104 -0
  93. package/lib/settings/capabilities.d.ts +1 -0
  94. package/lib/settings/capabilities.js +55 -0
  95. package/lib/settings/card.d.ts +31 -0
  96. package/lib/settings/card.js +105 -0
  97. package/lib/settings/code.d.ts +1 -0
  98. package/lib/settings/code.js +51 -0
  99. package/lib/settings/environments.d.ts +1 -0
  100. package/lib/settings/environments.js +53 -0
  101. package/lib/settings/general.d.ts +1 -0
  102. package/lib/settings/general.js +44 -0
  103. package/lib/settings/index.d.ts +4 -0
  104. package/lib/settings/index.js +22 -0
  105. package/lib/settings/integrations.d.ts +1 -0
  106. package/lib/settings/integrations.js +81 -0
  107. package/lib/settings/keys.d.ts +1 -0
  108. package/lib/settings/keys.js +102 -0
  109. package/lib/settings/machines.d.ts +1 -0
  110. package/lib/settings/machines.js +118 -0
  111. package/lib/settings/members.d.ts +1 -0
  112. package/lib/settings/members.js +67 -0
  113. package/lib/settings/memory.d.ts +1 -0
  114. package/lib/settings/memory.js +56 -0
  115. package/lib/settings/notifications.d.ts +1 -0
  116. package/lib/settings/notifications.js +97 -0
  117. package/lib/settings/privacy.d.ts +1 -0
  118. package/lib/settings/privacy.js +63 -0
  119. package/lib/settings/sections.d.ts +16 -0
  120. package/lib/settings/sections.js +32 -0
  121. package/lib/settings/ui.d.ts +49 -0
  122. package/lib/settings/ui.js +47 -0
  123. package/lib/settings/usage.d.ts +1 -0
  124. package/lib/settings/usage.js +114 -0
  125. package/lib/shelf.d.ts +6 -0
  126. package/lib/shelf.js +86 -16
  127. package/lib/switch.js +1 -2
  128. package/lib/transcript.d.ts +12 -0
  129. package/lib/transcript.js +56 -0
  130. package/lib/voice.d.ts +3 -1
  131. package/lib/voice.js +10 -4
  132. package/package.json +1 -1
  133. package/lib/account.d.ts +0 -14
  134. package/lib/account.js +0 -38
  135. package/lib/mcp.d.ts +0 -1
  136. package/lib/mcp.js +0 -28
@@ -0,0 +1,55 @@
1
+ /**
2
+ * A person's own API keys: one secret key (sk-, it belongs on a server) and one
3
+ * publishable key (pk-, safe in a page's source), at most one of each.
4
+ *
5
+ * GET /v1/account/keys {keys: [{type, prefix, key?, limit?, createdAt}]}
6
+ * POST /v1/account/keys {type, limit?} → {key, type, limit}, answered once
7
+ * DELETE /v1/account/keys?type=… revoke that one
8
+ *
9
+ * Creating is rotating: a new key of a type ends the one before it. A secret key
10
+ * is answered once and by its prefix after; a publishable key is public by
11
+ * construction and always comes back whole. A limit only narrows what a key may
12
+ * reach — `model:zen5`, `project:acme`, `product:commerce`, `model:*`.
13
+ */
14
+ import { call, query } from './call.js';
15
+ export const KINDS = ['secret', 'publishable'];
16
+ const str = (v) => (typeof v === 'string' ? v : '');
17
+ const list = (v) => (Array.isArray(v) ? v.filter((s) => typeof s === 'string' && s !== '') : []);
18
+ const kind = (v) => (KINDS.includes(v) ? v : null);
19
+ export async function keys(t) {
20
+ const r = await call(t, 'GET', '/v1/account/keys');
21
+ const rows = Array.isArray(r?.keys) ? r.keys : [];
22
+ const out = [];
23
+ for (const raw of rows) {
24
+ const o = (raw && typeof raw === 'object' ? raw : {});
25
+ const type = kind(o.type);
26
+ if (!type)
27
+ continue;
28
+ out.push({ type, prefix: str(o.prefix), key: str(o.key), limit: list(o.limit), created: str(o.createdAt) });
29
+ }
30
+ return out;
31
+ }
32
+ /** A `kind:name` limit: a lowercase kind, a colon, and a name or `*`. */
33
+ export const LIMIT = /^[a-z][a-z0-9-]*:[^\s,:]+$/;
34
+ /** The limits a person typed, split on commas and spaces; throws on the first that is not one. */
35
+ export function limits(raw) {
36
+ const out = raw
37
+ .split(/[\s,]+/)
38
+ .map((s) => s.trim())
39
+ .filter(Boolean);
40
+ const bad = out.find((s) => !LIMIT.test(s));
41
+ if (bad)
42
+ throw new Error(`${bad} is not a limit — write it as kind:name, like model:zen5`);
43
+ return out;
44
+ }
45
+ /** Create a key of this type, ending the one before it. The answer is the only time a secret key is shown. */
46
+ export async function mint(t, type, limit = []) {
47
+ const r = await call(t, 'POST', '/v1/account/keys', limit.length ? { type, limit } : { type });
48
+ const key = str(r?.key) || str(r?.accessKey);
49
+ if (!key)
50
+ throw new Error('The platform answered no key');
51
+ return { type: kind(r?.type) ?? type, key, limit: list(r?.limit) };
52
+ }
53
+ export async function revoke(t, type) {
54
+ await call(t, 'DELETE', `/v1/account/keys${query({ type })}`);
55
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * The org's machines: its own computers a run can be sent to, beside the sandbox.
3
+ *
4
+ * GET /v1/agent/targets {targets: [...]}, newest first
5
+ * POST /v1/agent/targets register one by hand {label, kind, host}
6
+ * PATCH /v1/agent/targets/{id} rename it, drain it, bring it back
7
+ * DELETE /v1/agent/targets/{id} deregister it
8
+ * POST /v1/agent/targets/{id}/key mint or rotate its claim key, answered once
9
+ *
10
+ * A machine belongs to the member who registered it. Only that member or an org
11
+ * admin changes, removes or keys it, and the platform answers anyone else the
12
+ * same 404 an unknown id gets — the record names no owner — so a 404 here is
13
+ * said as that rule. `hanzo link`, run on the machine, registers it under its
14
+ * hostname and keeps it online while it runs.
15
+ */
16
+ import { type Target } from './call.ts';
17
+ export declare const KINDS: readonly ['laptop', 'gpu', 'cloud', 'cluster', 'machine'];
18
+ export type Kind = (typeof KINDS)[number];
19
+ export interface Machine {
20
+ /** `tgt_…` */
21
+ id: string;
22
+ label: string;
23
+ /** laptop | gpu | cloud | cluster | machine */
24
+ kind: string;
25
+ /** online | offline | draining, as the platform judges it from the last heartbeat. */
26
+ status: string;
27
+ /** "10 vCPU / 122G / 1× GB10", when the machine reported one. */
28
+ capacity: string;
29
+ /** The hostname its runs report, or '' for one addressed by id only. */
30
+ host: string;
31
+ /** The org's runs mapped to it, and how many of those are running now. */
32
+ sessions: number;
33
+ running: number;
34
+ /** When it last beat, RFC 3339, or '' when it never has. */
35
+ seen: string;
36
+ }
37
+ export declare function machine(raw: unknown): Machine;
38
+ export declare function machines(t: Target): Promise<Machine[]>;
39
+ /** Register a machine by hand. `hanzo link` on a machine with this hostname takes the row over. */
40
+ export declare function add(t: Target, what: {
41
+ label: string;
42
+ kind: Kind;
43
+ host?: string;
44
+ }): Promise<Machine>;
45
+ /** Rename a machine, or drain it (`draining`) and bring it back (`online`). */
46
+ export declare function change(t: Target, id: string, what: {
47
+ label?: string;
48
+ status?: 'online' | 'draining';
49
+ }): Promise<Machine>;
50
+ export declare function remove(t: Target, id: string): Promise<void>;
51
+ /**
52
+ * Mint the machine's claim key, answered this once: only its hash is kept, and
53
+ * minting again ends the key before it. A runner on the machine sends it as
54
+ * `X-Target-Key` when it claims the runs sent there.
55
+ */
56
+ export declare function key(t: Target, id: string): Promise<string>;
@@ -0,0 +1,84 @@
1
+ /**
2
+ * The org's machines: its own computers a run can be sent to, beside the sandbox.
3
+ *
4
+ * GET /v1/agent/targets {targets: [...]}, newest first
5
+ * POST /v1/agent/targets register one by hand {label, kind, host}
6
+ * PATCH /v1/agent/targets/{id} rename it, drain it, bring it back
7
+ * DELETE /v1/agent/targets/{id} deregister it
8
+ * POST /v1/agent/targets/{id}/key mint or rotate its claim key, answered once
9
+ *
10
+ * A machine belongs to the member who registered it. Only that member or an org
11
+ * admin changes, removes or keys it, and the platform answers anyone else the
12
+ * same 404 an unknown id gets — the record names no owner — so a 404 here is
13
+ * said as that rule. `hanzo link`, run on the machine, registers it under its
14
+ * hostname and keeps it online while it runs.
15
+ */
16
+ import { call, Refusal, seg } from './call.js';
17
+ export const KINDS = ['laptop', 'gpu', 'cloud', 'cluster', 'machine'];
18
+ const str = (v) => (typeof v === 'string' ? v : '');
19
+ const num = (v) => (typeof v === 'number' && Number.isFinite(v) ? v : 0);
20
+ export function machine(raw) {
21
+ const o = (raw && typeof raw === 'object' ? raw : {});
22
+ return {
23
+ id: str(o.id),
24
+ label: str(o.label) || str(o.host) || str(o.id),
25
+ kind: str(o.kind),
26
+ status: str(o.status),
27
+ capacity: str(o.capacity),
28
+ host: str(o.host),
29
+ sessions: num(o.sessions),
30
+ running: num(o.running),
31
+ seen: str(o.metricsAt),
32
+ };
33
+ }
34
+ export async function machines(t) {
35
+ const raw = (await call(t, 'GET', '/v1/agent/targets')) ?? {};
36
+ return (Array.isArray(raw.targets) ? raw.targets : []).map(machine).filter((m) => m.id);
37
+ }
38
+ /** The platform's 404 for a machine that is someone else's, said as the rule it is. */
39
+ async function owned(p) {
40
+ try {
41
+ return await p;
42
+ }
43
+ catch (e) {
44
+ if (e instanceof Refusal && e.status === 404)
45
+ throw new Refusal(404, 'This machine is gone, or not yours to change: only the member who linked it, or an org admin, can.');
46
+ throw e;
47
+ }
48
+ }
49
+ /** Register a machine by hand. `hanzo link` on a machine with this hostname takes the row over. */
50
+ export async function add(t, what) {
51
+ const label = what.label.trim();
52
+ if (!label)
53
+ throw new Error('A machine needs a name');
54
+ const host = (what.host ?? '').trim();
55
+ return machine(await call(t, 'POST', '/v1/agent/targets', { label, kind: what.kind, ...(host ? { host } : {}) }));
56
+ }
57
+ /** Rename a machine, or drain it (`draining`) and bring it back (`online`). */
58
+ export async function change(t, id, what) {
59
+ const body = {};
60
+ if (what.label !== undefined) {
61
+ const label = what.label.trim();
62
+ if (!label)
63
+ throw new Error('A machine needs a name');
64
+ body.label = label;
65
+ }
66
+ if (what.status !== undefined)
67
+ body.status = what.status;
68
+ return machine(await owned(call(t, 'PATCH', `/v1/agent/targets/${seg(id)}`, body)));
69
+ }
70
+ export async function remove(t, id) {
71
+ await owned(call(t, 'DELETE', `/v1/agent/targets/${seg(id)}`));
72
+ }
73
+ /**
74
+ * Mint the machine's claim key, answered this once: only its hash is kept, and
75
+ * minting again ends the key before it. A runner on the machine sends it as
76
+ * `X-Target-Key` when it claims the runs sent there.
77
+ */
78
+ export async function key(t, id) {
79
+ const r = await owned(call(t, 'POST', `/v1/agent/targets/${seg(id)}/key`));
80
+ const k = str(r?.claimKey);
81
+ if (!k)
82
+ throw new Error('The platform answered no key');
83
+ return k;
84
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Who is in the organization, and who has been asked to join it, as Hanzo IAM
3
+ * holds them.
4
+ *
5
+ * GET /v1/iam/memberships?org= the roster: {status, data: [membership], data2: count}
6
+ * GET /v1/iam/invitations?owner= {invitations, total} (org admin)
7
+ * POST /v1/iam/invitations issue one (org admin)
8
+ * DELETE /v1/iam/invitations/{owner}/{name} withdraw one (org admin)
9
+ *
10
+ * IAM decides the scope from the credential: a member reads the roster of an org
11
+ * they belong to, and only an admin of the org reads or writes its invitations.
12
+ * An invitation is a code IAM redeems when someone signs up with it; IAM sends
13
+ * no email of its own.
14
+ */
15
+ import { type Target } from './call.ts';
16
+ export type Role = 'owner' | 'admin' | 'member';
17
+ export interface Member {
18
+ /** `<homeOrg>/<username>`, IAM's id for the person. */
19
+ user: string;
20
+ /** The username alone. */
21
+ name: string;
22
+ role: Role;
23
+ since: string;
24
+ }
25
+ export declare function member(raw: unknown): Member;
26
+ /** The org's people, owners first. Grants to a team or narrowed to one project are not the org's roster. */
27
+ export declare function roster(t: Target, org: string): Promise<Member[]>;
28
+ export interface Invitation {
29
+ owner: string;
30
+ name: string;
31
+ email: string;
32
+ /** What the invitee enters when they sign up. */
33
+ code: string;
34
+ /** How many may join with it, and how many have. */
35
+ seats: number;
36
+ used: number;
37
+ /** `Active` is redeemable; anything else is not. */
38
+ state: string;
39
+ created: string;
40
+ }
41
+ export declare function invitation(raw: unknown): Invitation;
42
+ export declare function invitations(t: Target, org: string): Promise<Invitation[]>;
43
+ /** An address that can receive an invitation. */
44
+ export declare const EMAIL: RegExp;
45
+ /** Invite one address into the org: one seat, redeemable now, pinned to that address. */
46
+ export declare function invite(t: Target, org: string, email: string): Promise<Invitation>;
47
+ export declare function revoke(t: Target, i: Pick<Invitation, 'owner' | 'name'>): Promise<void>;
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Who is in the organization, and who has been asked to join it, as Hanzo IAM
3
+ * holds them.
4
+ *
5
+ * GET /v1/iam/memberships?org= the roster: {status, data: [membership], data2: count}
6
+ * GET /v1/iam/invitations?owner= {invitations, total} (org admin)
7
+ * POST /v1/iam/invitations issue one (org admin)
8
+ * DELETE /v1/iam/invitations/{owner}/{name} withdraw one (org admin)
9
+ *
10
+ * IAM decides the scope from the credential: a member reads the roster of an org
11
+ * they belong to, and only an admin of the org reads or writes its invitations.
12
+ * An invitation is a code IAM redeems when someone signs up with it; IAM sends
13
+ * no email of its own.
14
+ */
15
+ import { call, query, seg } from './call.js';
16
+ const str = (v) => (typeof v === 'string' ? v : '');
17
+ const num = (v) => (typeof v === 'number' && Number.isFinite(v) ? v : 0);
18
+ const obj = (v) => (v && typeof v === 'object' ? v : {});
19
+ const arr = (v) => (Array.isArray(v) ? v : []);
20
+ const ROLES = ['owner', 'admin', 'member'];
21
+ export function member(raw) {
22
+ const m = obj(raw);
23
+ const user = str(m.user);
24
+ const role = str(m.role);
25
+ return {
26
+ user,
27
+ name: user.includes('/') ? user.slice(user.indexOf('/') + 1) : user,
28
+ role: ROLES.includes(role) ? role : 'member',
29
+ since: str(m.createdTime),
30
+ };
31
+ }
32
+ /** The org's people, owners first. Grants to a team or narrowed to one project are not the org's roster. */
33
+ export async function roster(t, org) {
34
+ const raw = obj(await call(t, 'GET', `/v1/iam/memberships${query({ org })}`));
35
+ if (raw.status === 'error')
36
+ throw new Error(str(raw.msg) || 'IAM refused the roster');
37
+ const rank = (r) => ROLES.indexOf(r);
38
+ return arr(raw.data)
39
+ .map(obj)
40
+ .filter((m) => str(m.user) && !str(m.workspace) && !str(m.project))
41
+ .map(member)
42
+ .sort((a, b) => rank(a.role) - rank(b.role) || a.name.localeCompare(b.name));
43
+ }
44
+ export function invitation(raw) {
45
+ const i = obj(raw);
46
+ return {
47
+ owner: str(i.owner),
48
+ name: str(i.name),
49
+ email: str(i.email),
50
+ code: str(i.code),
51
+ seats: num(i.quota),
52
+ used: num(i.usedCount),
53
+ state: str(i.state),
54
+ created: str(i.createdTime) || str(i.createdAt),
55
+ };
56
+ }
57
+ export async function invitations(t, org) {
58
+ const raw = obj(await call(t, 'GET', `/v1/iam/invitations${query({ owner: org })}`));
59
+ return arr(raw.invitations).map(invitation).filter((i) => i.name);
60
+ }
61
+ /** An address that can receive an invitation. */
62
+ export const EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
63
+ /** Random hex, from the browser's own generator. */
64
+ function hex(bytes) {
65
+ const b = new Uint8Array(bytes);
66
+ crypto.getRandomValues(b);
67
+ return Array.from(b, (x) => x.toString(16).padStart(2, '0')).join('');
68
+ }
69
+ /** Invite one address into the org: one seat, redeemable now, pinned to that address. */
70
+ export async function invite(t, org, email) {
71
+ const address = email.trim().toLowerCase();
72
+ if (!EMAIL.test(address))
73
+ throw new Error(`${email.trim() || 'That'} is not an email address`);
74
+ const local = address.slice(0, address.indexOf('@')).replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 24);
75
+ return invitation(await call(t, 'POST', '/v1/iam/invitations', {
76
+ owner: org,
77
+ name: `invite-${local || 'member'}-${hex(3)}`,
78
+ displayName: address,
79
+ email: address,
80
+ code: hex(8),
81
+ quota: 1,
82
+ usedCount: 0,
83
+ state: 'Active',
84
+ }));
85
+ }
86
+ export async function revoke(t, i) {
87
+ await call(t, 'DELETE', `/v1/iam/invitations/${seg(i.owner)}/${seg(i.name)}`);
88
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * What Hanzo remembers about the signed-in person, in the org they act in.
3
+ *
4
+ * GET /v1/ai/memory/list?limit= {status, data: [memory]}, newest first
5
+ * POST /v1/ai/memory/remember {content} → {status, data: memory}
6
+ * POST /v1/ai/memory/delete {id: "<owner>/<name>"}
7
+ *
8
+ * The platform keys every memory on the validated caller and org, never on the
9
+ * request, so these read and change the caller's own and nobody else's. A
10
+ * refusal arrives in the envelope, usually under a 200.
11
+ */
12
+ import { type Target } from './call.ts';
13
+ export interface Memory {
14
+ /** `<owner>/<name>`, what a delete names. */
15
+ id: string;
16
+ content: string;
17
+ /** user, fact, … as the platform files it. */
18
+ kind: string;
19
+ /** When it was last written, as the platform wrote it. */
20
+ updated: string;
21
+ }
22
+ export declare function memoryOf(raw: unknown): Memory | null;
23
+ export declare function memories(t: Target, limit?: number): Promise<Memory[]>;
24
+ export declare function remember(t: Target, content: string): Promise<Memory | null>;
25
+ export declare function forget(t: Target, id: string): Promise<void>;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * What Hanzo remembers about the signed-in person, in the org they act in.
3
+ *
4
+ * GET /v1/ai/memory/list?limit= {status, data: [memory]}, newest first
5
+ * POST /v1/ai/memory/remember {content} → {status, data: memory}
6
+ * POST /v1/ai/memory/delete {id: "<owner>/<name>"}
7
+ *
8
+ * The platform keys every memory on the validated caller and org, never on the
9
+ * request, so these read and change the caller's own and nobody else's. A
10
+ * refusal arrives in the envelope, usually under a 200.
11
+ */
12
+ import { call, query, unwrap } from './call.js';
13
+ const str = (v) => (typeof v === 'string' ? v : '');
14
+ export function memoryOf(raw) {
15
+ const o = (raw && typeof raw === 'object' ? raw : {});
16
+ const owner = str(o.owner);
17
+ const name = str(o.name);
18
+ if (!owner || !name)
19
+ return null;
20
+ return { id: `${owner}/${name}`, content: str(o.content), kind: str(o.kind), updated: str(o.updatedTime) || str(o.createdTime) };
21
+ }
22
+ export async function memories(t, limit = 100) {
23
+ const rows = unwrap(await call(t, 'GET', `/v1/ai/memory/list${query({ limit })}`));
24
+ return (Array.isArray(rows) ? rows : []).map(memoryOf).filter((m) => m !== null);
25
+ }
26
+ export async function remember(t, content) {
27
+ const said = content.trim();
28
+ if (!said)
29
+ throw new Error('Say what Hanzo should remember');
30
+ return memoryOf(unwrap(await call(t, 'POST', '/v1/ai/memory/remember', { content: said })));
31
+ }
32
+ export async function forget(t, id) {
33
+ unwrap(await call(t, 'POST', '/v1/ai/memory/delete', { id }));
34
+ }
@@ -1,13 +1,13 @@
1
1
  /**
2
2
  * Where a run can run: the platform's sandbox, or one of the org's machines.
3
3
  *
4
- * GET /v1/agent/targets → {targets: [...]}, newest first
4
+ * GET /v1/agent/targets → {targets: [...]}, newest first (machines.ts)
5
5
  *
6
6
  * The sandbox is not a row the platform lists — it is what a run gets when it
7
7
  * names no target — so it is the first place here and carries no id. A machine
8
8
  * can take a run only while it is online.
9
9
  */
10
- import { type Target } from './call.ts';
10
+ import type { Target } from './call.ts';
11
11
  export interface Place {
12
12
  /** `tgt_…`, or '' for the sandbox. */
13
13
  id: string;
package/lib/api/places.js CHANGED
@@ -1,31 +1,8 @@
1
- /**
2
- * Where a run can run: the platform's sandbox, or one of the org's machines.
3
- *
4
- * GET /v1/agent/targets → {targets: [...]}, newest first
5
- *
6
- * The sandbox is not a row the platform lists — it is what a run gets when it
7
- * names no target — so it is the first place here and carries no id. A machine
8
- * can take a run only while it is online.
9
- */
10
- import { call } from './call.js';
1
+ import { machines } from './machines.js';
11
2
  export const SANDBOX = { id: '', label: 'Hanzo sandbox', kind: 'sandbox', status: 'online', capacity: '' };
12
- const str = (v) => (typeof v === 'string' ? v : '');
13
3
  export async function places(t) {
14
- const raw = (await call(t, 'GET', '/v1/agent/targets')) ?? {};
15
- const rows = Array.isArray(raw.targets) ? raw.targets : [];
16
- const machines = rows
17
- .map((r) => {
18
- const o = (r && typeof r === 'object' ? r : {});
19
- return {
20
- id: str(o.id),
21
- label: str(o.label) || str(o.host) || str(o.id),
22
- kind: str(o.kind),
23
- status: str(o.status),
24
- capacity: str(o.capacity),
25
- };
26
- })
27
- .filter((p) => p.id);
28
- return [SANDBOX, ...machines];
4
+ const list = await machines(t);
5
+ return [SANDBOX, ...list.map(({ id, label, kind, status, capacity }) => ({ id, label, kind, status, capacity }))];
29
6
  }
30
7
  /** Whether a run sent here now would be taken. */
31
8
  export const ready = (p) => p.status === 'online';
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Plugins: TypeScript connectors an org builds, and what this deployment mounts.
3
+ *
4
+ * GET /v1/tool/plugins/authored {plugins: [plugin]}, newest first, each with its source
5
+ * POST /v1/tool/plugins/build {name, provider?, source | spec} → 201 {bytes, generated, plugin}
6
+ * DELETE /v1/tool/plugins/authored/{id} {deleted}
7
+ * GET /v1/tool/plugins {plugins: [{name, enabled, prefixes}]}
8
+ *
9
+ * Building is the gate: the platform bundles the source and compiles it in the
10
+ * runtime that will run it, and keeps it only if both succeed — a failure is a
11
+ * 422 whose reason is the bundler's. A spec (an OpenAPI document or prose) has a
12
+ * model write the source, which comes back to be read. A plugin never carries a
13
+ * credential: it names the connectors provider whose credential it reads when it
14
+ * runs. The mounted list is the deployment's own subsystems, read-only.
15
+ */
16
+ import { type Target } from './call.ts';
17
+ export interface Plugin {
18
+ /** What a delete addresses. */
19
+ id: string;
20
+ name: string;
21
+ /** The connectors provider whose credential it reads, or ''. */
22
+ provider: string;
23
+ /** The TypeScript as written or generated. */
24
+ source: string;
25
+ /** When it was last built, Unix seconds. */
26
+ built: number;
27
+ }
28
+ export interface Mount {
29
+ name: string;
30
+ enabled: boolean;
31
+ prefixes: string[];
32
+ }
33
+ export interface Built {
34
+ plugin: Plugin;
35
+ /** The size of the bundle the runtime executes. */
36
+ bytes: number;
37
+ /** Whether a model wrote the source from a spec. */
38
+ generated: boolean;
39
+ }
40
+ /** A plugin's name: one lowercase path segment, the id it runs by. */
41
+ export declare const NAME: RegExp;
42
+ export declare function plugin(raw: unknown): Plugin;
43
+ export declare function authored(t: Target): Promise<Plugin[]>;
44
+ export declare function mounted(t: Target): Promise<Mount[]>;
45
+ export interface Build {
46
+ name: string;
47
+ provider: string;
48
+ /** Exactly one of the two carries text. */
49
+ source: string;
50
+ spec: string;
51
+ }
52
+ /** Why a plugin cannot be built as it stands, or '' when it can. */
53
+ export declare function refuse(b: Build): string;
54
+ export declare function build(t: Target, b: Build): Promise<Built>;
55
+ export declare function remove(t: Target, id: string): Promise<void>;
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Plugins: TypeScript connectors an org builds, and what this deployment mounts.
3
+ *
4
+ * GET /v1/tool/plugins/authored {plugins: [plugin]}, newest first, each with its source
5
+ * POST /v1/tool/plugins/build {name, provider?, source | spec} → 201 {bytes, generated, plugin}
6
+ * DELETE /v1/tool/plugins/authored/{id} {deleted}
7
+ * GET /v1/tool/plugins {plugins: [{name, enabled, prefixes}]}
8
+ *
9
+ * Building is the gate: the platform bundles the source and compiles it in the
10
+ * runtime that will run it, and keeps it only if both succeed — a failure is a
11
+ * 422 whose reason is the bundler's. A spec (an OpenAPI document or prose) has a
12
+ * model write the source, which comes back to be read. A plugin never carries a
13
+ * credential: it names the connectors provider whose credential it reads when it
14
+ * runs. The mounted list is the deployment's own subsystems, read-only.
15
+ */
16
+ import { call, seg } from './call.js';
17
+ /** A plugin's name: one lowercase path segment, the id it runs by. */
18
+ export const NAME = /^[a-z0-9][a-z0-9_-]{0,63}$/;
19
+ const str = (v) => (typeof v === 'string' ? v : '');
20
+ const num = (v) => (typeof v === 'number' && Number.isFinite(v) ? v : 0);
21
+ const obj = (v) => (v && typeof v === 'object' ? v : {});
22
+ const rows = (v) => (Array.isArray(v) ? v : []);
23
+ export function plugin(raw) {
24
+ const o = obj(raw);
25
+ return { id: str(o.id), name: str(o.name), provider: str(o.provider), source: str(o.source), built: num(o.createdAt) };
26
+ }
27
+ export async function authored(t) {
28
+ return rows(obj(await call(t, 'GET', '/v1/tool/plugins/authored')).plugins).map(plugin).filter((p) => p.id);
29
+ }
30
+ export async function mounted(t) {
31
+ return rows(obj(await call(t, 'GET', '/v1/tool/plugins')).plugins)
32
+ .map((r) => {
33
+ const o = obj(r);
34
+ return { name: str(o.name), enabled: o.enabled === true, prefixes: rows(o.prefixes).map(str).filter(Boolean) };
35
+ })
36
+ .filter((m) => m.name);
37
+ }
38
+ /** Why a plugin cannot be built as it stands, or '' when it can. */
39
+ export function refuse(b) {
40
+ if (!NAME.test(b.name))
41
+ return 'A name is one lowercase word: letters, digits, _ or -';
42
+ if (!b.source.trim() === !b.spec.trim())
43
+ return 'Give the TypeScript or the API it calls, one of the two';
44
+ return '';
45
+ }
46
+ export async function build(t, b) {
47
+ const why = refuse(b);
48
+ if (why)
49
+ throw new Error(why);
50
+ const body = { name: b.name };
51
+ if (b.provider.trim())
52
+ body.provider = b.provider.trim();
53
+ if (b.source.trim())
54
+ body.source = b.source;
55
+ else
56
+ body.spec = b.spec;
57
+ const r = obj(await call(t, 'POST', '/v1/tool/plugins/build', body));
58
+ return { plugin: plugin(r.plugin), bytes: num(r.bytes), generated: r.generated === true };
59
+ }
60
+ export async function remove(t, id) {
61
+ await call(t, 'DELETE', `/v1/tool/plugins/authored/${seg(id)}`);
62
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * The signed-in person's own settings, as the platform keeps them for every
3
+ * Hanzo surface.
4
+ *
5
+ * GET /v1/pref → {prefs: {…}, updatedAt} an empty document for someone who saved nothing
6
+ * PATCH /v1/pref {key: value | null} a shallow merge; null deletes the key
7
+ *
8
+ * The document is one object shared by every surface, keyed per person and per
9
+ * org. This reads only the keys the builder owns and writes only those, so a
10
+ * key another surface saved is left as it was. A value this version does not
11
+ * recognise reads as unset rather than as a choice.
12
+ */
13
+ import { type Target } from './call.ts';
14
+ export type Theme = 'system' | 'light' | 'dark';
15
+ export type Text = 'small' | 'medium' | 'large';
16
+ export type Motion = 'system' | 'reduced';
17
+ /** Where and how a new run starts when New has no choice of its own kept. */
18
+ export interface Code {
19
+ model?: string;
20
+ effort?: 'low' | 'medium' | 'high';
21
+ mode?: 'build' | 'plan';
22
+ /** A machine's id, or '' for the sandbox. */
23
+ place?: string;
24
+ }
25
+ export interface Prefs {
26
+ theme?: Theme;
27
+ text?: Text;
28
+ motion?: Motion;
29
+ /** The dictation language, as voice.ts names it. */
30
+ language?: string;
31
+ /** What Hanzo calls the person. */
32
+ callName?: string;
33
+ /** What best describes their work. */
34
+ work?: string;
35
+ /** What every coding run is told about how they like to work. */
36
+ instructions?: string;
37
+ code?: Code;
38
+ }
39
+ /** A change: a key set to a value, or to null to delete it. */
40
+ export type Patch = {
41
+ [K in keyof Prefs]?: Prefs[K] | null;
42
+ };
43
+ export declare function code(raw: unknown): Code | undefined;
44
+ /** The builder's keys from a whole preference document. */
45
+ export declare function prefs(raw: unknown): Prefs;
46
+ /** What a document reads as once `patch` is merged into it — the platform's own rule. */
47
+ export declare function merge(base: Prefs, patch: Patch): Prefs;
48
+ export declare function read(t: Target): Promise<Prefs>;
49
+ export declare function save(t: Target, patch: Patch): Promise<Prefs>;