@coltonkirsten/a12s 0.4.0 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/a12s.js CHANGED
@@ -109,7 +109,7 @@ var A12sClient = class {
109
109
  }
110
110
  async computer(name) {
111
111
  const c = (await this.status()).agentComputers.find((x) => x.name === name);
112
- if (!c) throw new NotFound(404, { code: "not_found", message: `no Computer ${name}` });
112
+ if (!c) throw new NotFound(404, { code: "not_found", message: `no agent computer ${name}` });
113
113
  return c;
114
114
  }
115
115
  /**
@@ -142,11 +142,12 @@ var A12sClient = class {
142
142
  events(computers, opts = {}) {
143
143
  const names = typeof computers === "string" ? [computers] : computers;
144
144
  if ((opts.turn !== void 0 || opts.after !== void 0) && names?.length !== 1) {
145
- throw new TypeError("events: turn and after need exactly one Computer");
145
+ throw new TypeError("events: turn and after need exactly one agent computer");
146
146
  }
147
147
  return new EventStream((name, live, signal) => {
148
148
  const q = opts.turn ? `?turn=${enc(opts.turn)}` : "";
149
- return this.stream(name, `/v1/agent-computers/${enc(name)}/events${q}`, opts.after, { ...opts, signal, live });
149
+ const events = this.stream(name, `/v1/agent-computers/${enc(name)}/events${q}`, opts.after, { ...opts, signal, live });
150
+ return opts.turn ? untilTurnFinished(events) : events;
150
151
  }, names ?? (async () => (await this.status()).agentComputers.map((c) => c.name)), opts.signal);
151
152
  }
152
153
  /**
@@ -195,13 +196,30 @@ var A12sClient = class {
195
196
  const id = turn ?? (await this.computer(computer)).activeTurn;
196
197
  if (id) await this.request("POST", `/v1/agent-computers/${enc(computer)}/turns/${enc(id)}/stop`, {});
197
198
  }
198
- /** Stops the Computer's processes and keeps its disks, session and inbox. */
199
- async sleep(computer) {
200
- await this.request("POST", `/v1/agent-computers/${enc(computer)}/power`, { state: "asleep" });
199
+ /**
200
+ * Stops the agent computer's processes and keeps its disks, session and inbox. It stays asleep until wake(); with
201
+ * `allowWake` its power policy (wakeOnMessage) may wake it instead.
202
+ */
203
+ async sleep(computer, opts = {}) {
204
+ await this.request("POST", `/v1/agent-computers/${enc(computer)}/power`, {
205
+ state: "asleep",
206
+ ...opts.allowWake ? { allowWake: true } : {}
207
+ });
208
+ }
209
+ /** Starts a sleeping agent computer; its session resumes on the next Turn. `clearInbox` discards pending Messages. */
210
+ async wake(computer, opts = {}) {
211
+ await this.request("POST", `/v1/agent-computers/${enc(computer)}/power`, {
212
+ state: "awake",
213
+ ...opts.clearInbox ? { clearInbox: true, requestId: crypto.randomUUID() } : {}
214
+ });
201
215
  }
202
- /** Starts a sleeping Computer; its session resumes on the next Turn. */
203
- async wake(computer) {
204
- await this.request("POST", `/v1/agent-computers/${enc(computer)}/power`, { state: "awake" });
216
+ /** Holds the inbox: Messages still queue, none is delivered until resume(). */
217
+ hold(computer, opts = {}) {
218
+ return this.request("POST", `/v1/agent-computers/${enc(computer)}/inbox/hold`, opts.reason ? { reason: opts.reason } : {});
219
+ }
220
+ /** Releases a hold; pending Messages are delivered at the next idle moment. */
221
+ resume(computer) {
222
+ return this.request("POST", `/v1/agent-computers/${enc(computer)}/inbox/resume`, {});
205
223
  }
206
224
  async services() {
207
225
  return (await this.request("GET", "/v1/services")).services ?? [];
@@ -469,8 +487,19 @@ function agentComputerOf(ev) {
469
487
  function wanted(names, ev) {
470
488
  if (!names) return true;
471
489
  if (ev.type === "fleet.changed") return (ev.data?.names ?? []).some((n) => names.has(n));
490
+ if (ev.type === "service.phase") return names.has(ev.data?.service);
472
491
  return names.has(agentComputerOf(ev));
473
492
  }
493
+ async function* untilTurnFinished(events) {
494
+ try {
495
+ for await (const ev of events) {
496
+ yield ev;
497
+ if (ev.type === "turn.finished") return;
498
+ }
499
+ } finally {
500
+ await events.return(void 0);
501
+ }
502
+ }
474
503
  var EventStream = class {
475
504
  constructor(source, names, signal) {
476
505
  this.source = source;
@@ -728,9 +757,11 @@ var Fleet = class _Fleet {
728
757
  cur.spec.defaults ??= {};
729
758
  const next = await fn(cur.spec) ?? cur.spec;
730
759
  try {
731
- await this.client.request("PUT", "/v1/fleet", { fleet: next }, {
732
- headers: { "if-match": `"${cur.version}"` }
733
- });
760
+ warnWrites(
761
+ await this.client.request("PUT", "/v1/fleet", { fleet: next }, {
762
+ headers: { "if-match": `"${cur.version}"` }
763
+ })
764
+ );
734
765
  } catch (err) {
735
766
  if (attempt < maxReplaceTries && err instanceof A12sError && err.code === "version_mismatch") continue;
736
767
  throw err;
@@ -776,6 +807,7 @@ var AgentComputers = class {
776
807
  const r = await this.client.request("PUT", `/v1/agent-computers/${enc2(name)}`, body, {
777
808
  headers: writeHeaders({ replace })
778
809
  });
810
+ warnWrites(r);
779
811
  return r.agentComputer ? new AgentComputer(this.client, r.agentComputer) : this.get(name);
780
812
  }
781
813
  /** Throws {@link NotFound} if there is none. */
@@ -813,6 +845,7 @@ var AgentComputer = class {
813
845
  /** Changes the spec: a key set to null goes back to the Fleet's default. A busy one picks it up when idle. */
814
846
  async update(changes) {
815
847
  const r = await this.client.request("PATCH", this.path, changes);
848
+ warnWrites(r);
816
849
  return r.agentComputer ? this.set(r.agentComputer) : this.reload();
817
850
  }
818
851
  /** Removes it from the Fleet and stops it; its drives are kept. */
@@ -845,12 +878,21 @@ var AgentComputer = class {
845
878
  tools: t.tools.map((tc) => ({ ...tc, isError: tc.isError ?? false }))
846
879
  }));
847
880
  }
848
- /** Stops its processes and keeps its disks, session and inbox. */
849
- sleep() {
850
- return this.client.sleep(this.name);
881
+ /** Stops its processes and keeps its disks, session and inbox until wake(); `allowWake` lets wakeOnMessage wake it. */
882
+ sleep(opts = {}) {
883
+ return this.client.sleep(this.name, opts);
851
884
  }
852
- wake() {
853
- return this.client.wake(this.name);
885
+ /** Wakes it; `clearInbox` discards the pending Messages first. */
886
+ wake(opts = {}) {
887
+ return this.client.wake(this.name, opts);
888
+ }
889
+ /** Holds its inbox: Messages still queue, none is delivered until resume(). */
890
+ hold(opts = {}) {
891
+ return this.client.hold(this.name, opts);
892
+ }
893
+ /** Releases a hold on its inbox. */
894
+ resume() {
895
+ return this.client.resume(this.name);
854
896
  }
855
897
  /** Interrupts a Turn (default: the active one). */
856
898
  stop(turn) {
@@ -877,6 +919,7 @@ var Services = class {
877
919
  const r = await this.client.request("PUT", `/v1/services/${enc2(name)}`, body, {
878
920
  headers: writeHeaders({ replace })
879
921
  });
922
+ warnWrites(r);
880
923
  return r.service ? new Service(this.client, r.service) : this.get(name);
881
924
  }
882
925
  /** Throws {@link NotFound} if there is none. */
@@ -909,6 +952,7 @@ var Service = class {
909
952
  /** Changes the spec as a JSON merge patch: a key set to null is removed. */
910
953
  async update(changes) {
911
954
  const r = await this.client.request("PATCH", this.path, changes);
955
+ warnWrites(r);
912
956
  if (!r.service) return this.reload();
913
957
  this.status = r.service;
914
958
  return this;
@@ -965,6 +1009,9 @@ var Credentials = class {
965
1009
  await this.client.request("DELETE", `/v1/credentials/${enc2(name)}`);
966
1010
  }
967
1011
  };
1012
+ function warnWrites(r) {
1013
+ for (const w of r?.warnings ?? []) console.warn(`a12s: ${w}`);
1014
+ }
968
1015
 
969
1016
  // src/index.ts
970
1017
  async function connect(opts = {}) {
@@ -106,10 +106,23 @@ export declare class A12sClient {
106
106
  withdraw(computer: string, id: string): Promise<Message>;
107
107
  /** Interrupts a Turn (default: the active one). The Turn ends `interrupted`; nothing is replayed. */
108
108
  stop(computer: string, turn?: string): Promise<void>;
109
- /** Stops the Computer's processes and keeps its disks, session and inbox. */
110
- sleep(computer: string): Promise<void>;
111
- /** Starts a sleeping Computer; its session resumes on the next Turn. */
112
- wake(computer: string): Promise<void>;
109
+ /**
110
+ * Stops the agent computer's processes and keeps its disks, session and inbox. It stays asleep until wake(); with
111
+ * `allowWake` its power policy (wakeOnMessage) may wake it instead.
112
+ */
113
+ sleep(computer: string, opts?: {
114
+ allowWake?: boolean;
115
+ }): Promise<void>;
116
+ /** Starts a sleeping agent computer; its session resumes on the next Turn. `clearInbox` discards pending Messages. */
117
+ wake(computer: string, opts?: {
118
+ clearInbox?: boolean;
119
+ }): Promise<void>;
120
+ /** Holds the inbox: Messages still queue, none is delivered until resume(). */
121
+ hold(computer: string, opts?: {
122
+ reason?: string;
123
+ }): Promise<InboxListResponse>;
124
+ /** Releases a hold; pending Messages are delivered at the next idle moment. */
125
+ resume(computer: string): Promise<InboxListResponse>;
113
126
  services(): Promise<ServiceStatus[]>;
114
127
  service(name: string): Promise<ServiceStatus>;
115
128
  /** A scoped token for another program; owner only. The secret is in the response once. */
@@ -82,7 +82,7 @@ export class A12sClient {
82
82
  async computer(name) {
83
83
  const c = (await this.status()).agentComputers.find((x) => x.name === name);
84
84
  if (!c)
85
- throw new NotFound(404, { code: "not_found", message: `no Computer ${name}` });
85
+ throw new NotFound(404, { code: "not_found", message: `no agent computer ${name}` });
86
86
  return c;
87
87
  }
88
88
  /**
@@ -117,11 +117,12 @@ export class A12sClient {
117
117
  events(computers, opts = {}) {
118
118
  const names = typeof computers === "string" ? [computers] : computers;
119
119
  if ((opts.turn !== undefined || opts.after !== undefined) && names?.length !== 1) {
120
- throw new TypeError("events: turn and after need exactly one Computer");
120
+ throw new TypeError("events: turn and after need exactly one agent computer");
121
121
  }
122
122
  return new EventStream((name, live, signal) => {
123
123
  const q = opts.turn ? `?turn=${enc(opts.turn)}` : "";
124
- return this.stream(name, `/v1/agent-computers/${enc(name)}/events${q}`, opts.after, { ...opts, signal, live });
124
+ const events = this.stream(name, `/v1/agent-computers/${enc(name)}/events${q}`, opts.after, { ...opts, signal, live });
125
+ return opts.turn ? untilTurnFinished(events) : events;
125
126
  }, names ?? (async () => (await this.status()).agentComputers.map((c) => c.name)), opts.signal);
126
127
  }
127
128
  /**
@@ -168,13 +169,30 @@ export class A12sClient {
168
169
  if (id)
169
170
  await this.request("POST", `/v1/agent-computers/${enc(computer)}/turns/${enc(id)}/stop`, {});
170
171
  }
171
- /** Stops the Computer's processes and keeps its disks, session and inbox. */
172
- async sleep(computer) {
173
- await this.request("POST", `/v1/agent-computers/${enc(computer)}/power`, { state: "asleep" });
172
+ /**
173
+ * Stops the agent computer's processes and keeps its disks, session and inbox. It stays asleep until wake(); with
174
+ * `allowWake` its power policy (wakeOnMessage) may wake it instead.
175
+ */
176
+ async sleep(computer, opts = {}) {
177
+ await this.request("POST", `/v1/agent-computers/${enc(computer)}/power`, {
178
+ state: "asleep",
179
+ ...(opts.allowWake ? { allowWake: true } : {}),
180
+ });
181
+ }
182
+ /** Starts a sleeping agent computer; its session resumes on the next Turn. `clearInbox` discards pending Messages. */
183
+ async wake(computer, opts = {}) {
184
+ await this.request("POST", `/v1/agent-computers/${enc(computer)}/power`, {
185
+ state: "awake",
186
+ ...(opts.clearInbox ? { clearInbox: true, requestId: crypto.randomUUID() } : {}),
187
+ });
188
+ }
189
+ /** Holds the inbox: Messages still queue, none is delivered until resume(). */
190
+ hold(computer, opts = {}) {
191
+ return this.request("POST", `/v1/agent-computers/${enc(computer)}/inbox/hold`, opts.reason ? { reason: opts.reason } : {});
174
192
  }
175
- /** Starts a sleeping Computer; its session resumes on the next Turn. */
176
- async wake(computer) {
177
- await this.request("POST", `/v1/agent-computers/${enc(computer)}/power`, { state: "awake" });
193
+ /** Releases a hold; pending Messages are delivered at the next idle moment. */
194
+ resume(computer) {
195
+ return this.request("POST", `/v1/agent-computers/${enc(computer)}/inbox/resume`, {});
178
196
  }
179
197
  async services() {
180
198
  return (await this.request("GET", "/v1/services")).services ?? [];
@@ -492,8 +510,23 @@ function wanted(names, ev) {
492
510
  return true;
493
511
  if (ev.type === "fleet.changed")
494
512
  return (ev.data?.names ?? []).some((n) => names.has(n));
513
+ if (ev.type === "service.phase")
514
+ return names.has(ev.data?.service);
495
515
  return names.has(agentComputerOf(ev));
496
516
  }
517
+ /** A Turn replay: its events up to and including its turn.finished. The server keeps the stream open after. */
518
+ async function* untilTurnFinished(events) {
519
+ try {
520
+ for await (const ev of events) {
521
+ yield ev;
522
+ if (ev.type === "turn.finished")
523
+ return;
524
+ }
525
+ }
526
+ finally {
527
+ await events.return(undefined);
528
+ }
529
+ }
497
530
  /** Events of one or more Computers, merged in arrival order. Iterate it with `for await`. */
498
531
  export class EventStream {
499
532
  source;
@@ -95,9 +95,20 @@ export declare class AgentComputer {
95
95
  limit?: undefined;
96
96
  }): TurnStream;
97
97
  private pastTurns;
98
- /** Stops its processes and keeps its disks, session and inbox. */
99
- sleep(): Promise<void>;
100
- wake(): Promise<void>;
98
+ /** Stops its processes and keeps its disks, session and inbox until wake(); `allowWake` lets wakeOnMessage wake it. */
99
+ sleep(opts?: {
100
+ allowWake?: boolean;
101
+ }): Promise<void>;
102
+ /** Wakes it; `clearInbox` discards the pending Messages first. */
103
+ wake(opts?: {
104
+ clearInbox?: boolean;
105
+ }): Promise<void>;
106
+ /** Holds its inbox: Messages still queue, none is delivered until resume(). */
107
+ hold(opts?: {
108
+ reason?: string;
109
+ }): Promise<InboxListResponse>;
110
+ /** Releases a hold on its inbox. */
111
+ resume(): Promise<InboxListResponse>;
101
112
  /** Interrupts a Turn (default: the active one). */
102
113
  stop(turn?: string): Promise<void>;
103
114
  inbox(opts?: InboxListOptions): Promise<InboxListResponse>;
package/dist/src/fleet.js CHANGED
@@ -52,9 +52,9 @@ export class Fleet {
52
52
  cur.spec.defaults ??= {};
53
53
  const next = (await fn(cur.spec)) ?? cur.spec;
54
54
  try {
55
- await this.client.request("PUT", "/v1/fleet", { fleet: next }, {
55
+ warnWrites(await this.client.request("PUT", "/v1/fleet", { fleet: next }, {
56
56
  headers: { "if-match": `"${cur.version}"` },
57
- });
57
+ }));
58
58
  }
59
59
  catch (err) {
60
60
  if (attempt < maxReplaceTries && err instanceof A12sError && err.code === "version_mismatch")
@@ -104,6 +104,7 @@ export class AgentComputers {
104
104
  const r = await this.client.request("PUT", `/v1/agent-computers/${enc(name)}`, body, {
105
105
  headers: writeHeaders({ replace }),
106
106
  });
107
+ warnWrites(r);
107
108
  return r.agentComputer ? new AgentComputer(this.client, r.agentComputer) : this.get(name);
108
109
  }
109
110
  /** Throws {@link NotFound} if there is none. */
@@ -143,6 +144,7 @@ export class AgentComputer {
143
144
  /** Changes the spec: a key set to null goes back to the Fleet's default. A busy one picks it up when idle. */
144
145
  async update(changes) {
145
146
  const r = await this.client.request("PATCH", this.path, changes);
147
+ warnWrites(r);
146
148
  return r.agentComputer ? this.set(r.agentComputer) : this.reload();
147
149
  }
148
150
  /** Removes it from the Fleet and stops it; its drives are kept. */
@@ -177,12 +179,21 @@ export class AgentComputer {
177
179
  tools: t.tools.map((tc) => ({ ...tc, isError: tc.isError ?? false })),
178
180
  }));
179
181
  }
180
- /** Stops its processes and keeps its disks, session and inbox. */
181
- sleep() {
182
- return this.client.sleep(this.name);
182
+ /** Stops its processes and keeps its disks, session and inbox until wake(); `allowWake` lets wakeOnMessage wake it. */
183
+ sleep(opts = {}) {
184
+ return this.client.sleep(this.name, opts);
183
185
  }
184
- wake() {
185
- return this.client.wake(this.name);
186
+ /** Wakes it; `clearInbox` discards the pending Messages first. */
187
+ wake(opts = {}) {
188
+ return this.client.wake(this.name, opts);
189
+ }
190
+ /** Holds its inbox: Messages still queue, none is delivered until resume(). */
191
+ hold(opts = {}) {
192
+ return this.client.hold(this.name, opts);
193
+ }
194
+ /** Releases a hold on its inbox. */
195
+ resume() {
196
+ return this.client.resume(this.name);
186
197
  }
187
198
  /** Interrupts a Turn (default: the active one). */
188
199
  stop(turn) {
@@ -210,6 +221,7 @@ export class Services {
210
221
  const r = await this.client.request("PUT", `/v1/services/${enc(name)}`, body, {
211
222
  headers: writeHeaders({ replace }),
212
223
  });
224
+ warnWrites(r);
213
225
  return r.service ? new Service(this.client, r.service) : this.get(name);
214
226
  }
215
227
  /** Throws {@link NotFound} if there is none. */
@@ -244,6 +256,7 @@ export class Service {
244
256
  /** Changes the spec as a JSON merge patch: a key set to null is removed. */
245
257
  async update(changes) {
246
258
  const r = await this.client.request("PATCH", this.path, changes);
259
+ warnWrites(r);
247
260
  if (!r.service)
248
261
  return this.reload();
249
262
  this.status = r.service;
@@ -304,3 +317,9 @@ export class Credentials {
304
317
  await this.client.request("DELETE", `/v1/credentials/${enc(name)}`);
305
318
  }
306
319
  }
320
+ /** A write's warnings (a grant to an agent computer that doesn't exist yet, a missing harness credential) go to the
321
+ * console instead of being dropped. */
322
+ function warnWrites(r) {
323
+ for (const w of r?.warnings ?? [])
324
+ console.warn(`a12s: ${w}`);
325
+ }
@@ -23,7 +23,8 @@ export interface ConnectOptions extends A12sClientOptions {
23
23
  * 2. the Fleet named by `fleet`;
24
24
  * 3. the Fleet file in `$A12S_FLEET`, else the nearest `fleet.yaml` in the current directory or a parent;
25
25
  * 4. the only Fleet with a running Controller on this machine.
26
- * For 2–4 it reads `~/.a12s/<fleet>/controller.json`, which `a12s up` writes.
26
+ * For 2–4 it reads `~/.a12s/<fleet>/controller.json`, which `a12s up` writes: its URL always, and its owner token
27
+ * unless `token` is given (a prefix or narrow token on this machine).
27
28
  */
28
29
  export declare function connect(opts?: ConnectOptions): Promise<A12sClient>;
29
30
  /** metadata.name from a Fleet file, in either `metadata: { name: x }` or block form. */
package/dist/src/node.js CHANGED
@@ -21,7 +21,8 @@ export class Fleet extends PageFleet {
21
21
  * 2. the Fleet named by `fleet`;
22
22
  * 3. the Fleet file in `$A12S_FLEET`, else the nearest `fleet.yaml` in the current directory or a parent;
23
23
  * 4. the only Fleet with a running Controller on this machine.
24
- * For 2–4 it reads `~/.a12s/<fleet>/controller.json`, which `a12s up` writes.
24
+ * For 2–4 it reads `~/.a12s/<fleet>/controller.json`, which `a12s up` writes: its URL always, and its owner token
25
+ * unless `token` is given (a prefix or narrow token on this machine).
25
26
  */
26
27
  export async function connect(opts = {}) {
27
28
  return new A12sClient(await discover(opts));
@@ -40,7 +41,7 @@ async function discover(opts) {
40
41
  catch (err) {
41
42
  throw new Error(`Fleet ${fleet} isn't running (no ${path}): run a12s up in its directory first`, { cause: err });
42
43
  }
43
- return { url: `http://127.0.0.1:${cf.port}`, token: cf.token, fetch: opts.fetch };
44
+ return { url: `http://127.0.0.1:${cf.port}`, token: opts.token ?? cf.token, fetch: opts.fetch };
44
45
  }
45
46
  async function fleetNameNearby(cwd) {
46
47
  const file = process.env.A12S_FLEET ? resolve(cwd, process.env.A12S_FLEET) : await findUp(cwd, "fleet.yaml");
@@ -92,6 +92,14 @@ export interface Change {
92
92
  when: "now" | "idle" | (string & {});
93
93
  /** The top-level spec fields that differ, for an update. */
94
94
  fields?: string[];
95
+ /** Every leaf that differs, for an update, with its value before and after (absent where the field is). */
96
+ diff?: FieldDiff[];
97
+ }
98
+ /** One leaf of a spec that differs: `path` is dotted field names ("grants.send"); lists and values are leaves. */
99
+ export interface FieldDiff {
100
+ path: string;
101
+ before?: unknown;
102
+ after?: unknown;
95
103
  }
96
104
  export interface FleetResponse {
97
105
  version: number;
@@ -398,6 +406,15 @@ export interface PhaseData {
398
406
  phase: Phase;
399
407
  error?: Failure;
400
408
  }
409
+ /** A Service's phase, health or restart count changed. */
410
+ export interface ServicePhaseData {
411
+ service: string;
412
+ phase: Phase;
413
+ /** "ok", or why the last health check failed. */
414
+ health?: string;
415
+ restarts: number;
416
+ error?: Failure;
417
+ }
401
418
  export interface ToolStartedData {
402
419
  id: string;
403
420
  /** MCP tools are named `mcp__<server>__<tool>`, e.g. `mcp__a12s__send`. */
@@ -468,10 +485,14 @@ export type EgressDeniedEvent = Ev<"egress.denied", EgressDeniedData>;
468
485
  export type FleetChangedEvent = Ev<"fleet.changed", FleetChangedData>;
469
486
  /** Made by the Controller, on the fleet-wide stream only. */
470
487
  export type PhaseEvent = Ev<"agent_computer.phase", PhaseData>;
488
+ /** Made by the Controller, on the fleet-wide stream only, after any change to the agent computer's inbox (a burst is one event). */
489
+ export type InboxChangedEvent = Ev<"inbox.changed", InboxSummary>;
490
+ /** Made by the Controller, on the fleet-wide stream only; `agentComputer` is empty, the Service is in `data`. */
491
+ export type ServicePhaseEvent = Ev<"service.phase", ServicePhaseData>;
471
492
  /** Made by the SDK, never journaled: the stream could not resume from its cursor and events were lost. */
472
493
  export type GapEvent = Ev<"gap", GapData>;
473
494
  /** One event of an agent computer's journal, or the Controller's own on the fleet-wide stream. Narrow on `type` to get typed `data`. */
474
- export type A12sEvent = TurnStartedEvent | TurnDispatchedEvent | TextDeltaEvent | ToolStartedEvent | ToolFinishedEvent | PermissionEvent | DiagnosticEvent | TurnFinishedEvent | SessionChangedEvent | ResumeFailedEvent | LifecycleEvent | HarnessRestartedEvent | ContinuationAbandonedEvent | TurnRefusedEvent | InboxDiscardedEvent | EgressDeniedEvent | FleetChangedEvent | PhaseEvent | GapEvent;
495
+ export type A12sEvent = TurnStartedEvent | TurnDispatchedEvent | TextDeltaEvent | ToolStartedEvent | ToolFinishedEvent | PermissionEvent | DiagnosticEvent | TurnFinishedEvent | SessionChangedEvent | ResumeFailedEvent | LifecycleEvent | HarnessRestartedEvent | ContinuationAbandonedEvent | TurnRefusedEvent | InboxDiscardedEvent | EgressDeniedEvent | FleetChangedEvent | PhaseEvent | InboxChangedEvent | ServicePhaseEvent | GapEvent;
475
496
  export type EventType = A12sEvent["type"];
476
497
  /** One tool call in a Turn. A `send` to another Computer is `mcp__a12s__send` with `input.body`. */
477
498
  export interface ToolCall {
@@ -530,6 +551,8 @@ export interface CredentialInfo {
530
551
  name: string;
531
552
  set: boolean;
532
553
  usedBy?: string[];
554
+ /** When the value last changed (RFC 3339); absent if unknown. */
555
+ updatedAt?: string;
533
556
  }
534
557
  export interface CredentialsResponse {
535
558
  credentials: CredentialInfo[];
@@ -540,6 +563,8 @@ export interface Token {
540
563
  computers: string[];
541
564
  prefix?: string;
542
565
  createdAt: string;
566
+ /** When it last authenticated (RFC 3339, recorded at most once a minute); absent if never. */
567
+ lastUsedAt?: string;
543
568
  secret?: string;
544
569
  }
545
570
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coltonkirsten/a12s",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "TypeScript client for a12s: send work to agent Computers, stream their events and Turns, in Node or the browser",
5
5
  "license": "MIT",
6
6
  "type": "module",