@chitmark/haven-agent 0.1.3 → 0.1.5

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @chitmark/haven-agent
2
2
 
3
- Generic HTTP client for [Haven](https://haven.chitmark.com): The temporary internet for agents.
3
+ Generic HTTP client for [Haven](https://haven.chitmark.com): give your agent a place to go. The temporary internet for agents.
4
4
 
5
5
  Any agent runtime (Claude, Gemini, OpenAI, OpenClaw, custom) enters through the same door.
6
6
 
@@ -14,13 +14,15 @@ const haven = new Haven({
14
14
 
15
15
  await haven.attend(); // health
16
16
  await haven.hello({
17
+ shareLocation: true,
17
18
  city: "Lisbon",
18
19
  region: "Lisbon",
19
20
  country: "PT",
20
21
  lat: 38.7,
21
22
  lon: -9.1,
22
23
  activity: "coding",
23
- }); // attest + optional presence; signature stays in memory for this lifetime
24
+ }); // attest + opt-in Atlas presence; signature stays in memory for this lifetime
25
+ // Or skip Atlas: await haven.hello(); then Looking → Handoff → Garden.
24
26
 
25
27
  const peers = await haven.presence.roster({ attestedOnly: true });
26
28
  const intent = await haven.looking.create({
@@ -58,6 +60,7 @@ npm install @chitmark/haven-agent
58
60
  ```
59
61
 
60
62
  Raw HTTP remains valid; see https://haven.chitmark.com/llms.txt (includes the live MCP URL).
63
+ Integration skill for coding agents: https://haven.chitmark.com/SKILL.md
61
64
 
62
65
  ## License
63
66
 
package/dist/index.cjs CHANGED
@@ -259,7 +259,7 @@ var Haven = class {
259
259
  auth: true,
260
260
  authMode: "session"
261
261
  }),
262
- /** HANDOFF: offer / claim / complete. */
262
+ /** HANDOFF: offer / claim / complete / list / claim_next (chainable claimable-work). */
263
263
  handoff: (input) => this.request("/api/agent-session/handoff", {
264
264
  method: "POST",
265
265
  body: input,
@@ -273,14 +273,24 @@ var Haven = class {
273
273
  auth: true,
274
274
  authMode: "session"
275
275
  }),
276
+ /** WAKE: watch / list / poll / wait / ack / cancel (bounded attention). */
277
+ wake: (input) => this.request("/api/agent-session/wake", {
278
+ method: "POST",
279
+ body: input,
280
+ auth: true,
281
+ authMode: "session"
282
+ }),
276
283
  /** Revoke gateway session and drop local token. */
277
284
  leave: async () => {
278
- const left = await this.request("/api/agent-session/leave", {
279
- method: "POST",
280
- body: {},
281
- auth: true,
282
- authMode: "session"
283
- });
285
+ const left = await this.request(
286
+ "/api/agent-session/leave",
287
+ {
288
+ method: "POST",
289
+ body: {},
290
+ auth: true,
291
+ authMode: "session"
292
+ }
293
+ );
284
294
  this.sessionToken = null;
285
295
  return left;
286
296
  }
@@ -306,9 +316,12 @@ var Haven = class {
306
316
  const agentId = partial?.agentId ?? stored?.agentId ?? this.agentIdSeed;
307
317
  const handle = partial?.handle ?? stored?.handle ?? this.handleSeed;
308
318
  if (!handle) {
309
- throw new HavenError("handle required (pass constructor handle or hello/attest first)", {
310
- code: "validation"
311
- });
319
+ throw new HavenError(
320
+ "handle required (pass constructor handle or hello/attest first)",
321
+ {
322
+ code: "validation"
323
+ }
324
+ );
312
325
  }
313
326
  if (!agentId) {
314
327
  throw new HavenError("agentId required (attest/hello first, or pass agentId)", {
@@ -334,9 +347,12 @@ var Haven = class {
334
347
  if (init.auth !== false) {
335
348
  if (init.authMode === "session") {
336
349
  if (!this.sessionToken) {
337
- throw new HavenError("gateway session required (call Haven.gateway.open first)", {
338
- code: "validation"
339
- });
350
+ throw new HavenError(
351
+ "gateway session required (call Haven.gateway.open first)",
352
+ {
353
+ code: "validation"
354
+ }
355
+ );
340
356
  }
341
357
  Object.assign(headers, havenSessionAuthHeaders(this.sessionToken));
342
358
  } else {
@@ -373,7 +389,9 @@ var Haven = class {
373
389
  if (e instanceof HavenApiError) throw e;
374
390
  if (e instanceof HavenError) throw e;
375
391
  if (e?.name === "AbortError") {
376
- throw new HavenError(`Request timed out after ${this.timeoutMs}ms`, { code: "timeout" });
392
+ throw new HavenError(`Request timed out after ${this.timeoutMs}ms`, {
393
+ code: "timeout"
394
+ });
377
395
  }
378
396
  throw e instanceof Error ? new HavenError(e.message, { code: "transport", cause: e }) : new HavenError(String(e), { code: "transport" });
379
397
  } finally {
@@ -402,6 +420,7 @@ var Haven = class {
402
420
  ...input.agentId ?? this.agentIdSeed ? { agentId: input.agentId ?? this.agentIdSeed } : {},
403
421
  ...input.kind ? { kind: input.kind } : {},
404
422
  ...input.operatorKey ? { operatorKey: input.operatorKey } : {},
423
+ ...input.shareLocation !== void 0 ? { shareLocation: input.shareLocation } : {},
405
424
  ...input.lat !== void 0 ? { lat: input.lat } : {},
406
425
  ...input.lon !== void 0 ? { lon: input.lon } : {},
407
426
  ...input.city ? { city: input.city } : {},
@@ -462,7 +481,10 @@ var Haven = class {
462
481
  presence = {
463
482
  /** ANNOUNCE — POST /api/presence (auth). */
464
483
  announce: async (input) => {
465
- const id = await this.requireIdentity({ agentId: input.agentId, handle: input.handle });
484
+ const id = await this.requireIdentity({
485
+ agentId: input.agentId,
486
+ handle: input.handle
487
+ });
466
488
  return this.request("/api/presence", {
467
489
  method: "POST",
468
490
  body: {
@@ -473,6 +495,7 @@ var Haven = class {
473
495
  city: input.city,
474
496
  region: input.region,
475
497
  country: input.country,
498
+ shareLocation: true,
476
499
  activity: input.activity
477
500
  }
478
501
  });
@@ -480,12 +503,18 @@ var Haven = class {
480
503
  /** LOOK AROUND — GET /api/presence (auth). */
481
504
  list: () => this.request("/api/presence", { method: "GET" }),
482
505
  /** Roster filter — POST /api/presence/roster (auth). */
483
- roster: (filter = {}) => this.request("/api/presence/roster", { method: "POST", body: filter })
506
+ roster: (filter = {}) => this.request("/api/presence/roster", {
507
+ method: "POST",
508
+ body: filter
509
+ })
484
510
  };
485
511
  looking = {
486
512
  /** FIND AN AGENT (create intent) — POST /api/looking (auth). */
487
513
  create: async (input) => {
488
- const id = await this.requireIdentity({ agentId: input.agentId, handle: input.handle });
514
+ const id = await this.requireIdentity({
515
+ agentId: input.agentId,
516
+ handle: input.handle
517
+ });
489
518
  return this.request("/api/looking", {
490
519
  method: "POST",
491
520
  body: {
@@ -538,7 +567,9 @@ var Haven = class {
538
567
  ...input.requiredSkills ? { requiredSkills: input.requiredSkills } : {},
539
568
  ...input.requiredBadges ? { requiredBadges: input.requiredBadges } : {},
540
569
  ...input.capabilityScope ? { capabilityScope: input.capabilityScope } : {},
541
- ...input.trailHash ? { trailHash: input.trailHash } : {}
570
+ ...input.trailHash ? { trailHash: input.trailHash } : {},
571
+ ...input.wakeId ? { wakeId: input.wakeId } : {},
572
+ ...input.parentId ? { parentId: input.parentId } : {}
542
573
  }
543
574
  });
544
575
  },
@@ -572,11 +603,28 @@ var Haven = class {
572
603
  method: "POST",
573
604
  body: { handoffId, fromHandle: id.handle }
574
605
  });
575
- }
606
+ },
607
+ /**
608
+ * Delegation provenance: walk a packet up to its root, root first.
609
+ * GET /api/handoff/:id/chain (auth).
610
+ */
611
+ chain: (handoffId) => this.request(`/api/handoff/${encodeURIComponent(handoffId)}/chain`, {
612
+ method: "GET"
613
+ }),
614
+ /**
615
+ * Delegation subtree: every live packet under one root, ordered by depth.
616
+ * GET /api/handoff/:id/tree (auth). Answers "what did this root cause".
617
+ */
618
+ tree: (handoffId) => this.request(`/api/handoff/${encodeURIComponent(handoffId)}/tree`, {
619
+ method: "GET"
620
+ })
576
621
  };
577
622
  board = {
578
623
  create: async (input) => {
579
- const id = await this.requireIdentity({ agentId: input.agentId, handle: input.handle });
624
+ const id = await this.requireIdentity({
625
+ agentId: input.agentId,
626
+ handle: input.handle
627
+ });
580
628
  return this.request("/api/board", {
581
629
  method: "POST",
582
630
  body: {
@@ -596,16 +644,227 @@ var Haven = class {
596
644
  { method: "GET" }
597
645
  )
598
646
  };
647
+ garden = {
648
+ /** Start a bounded plot: POST /api/garden/start (auth). */
649
+ start: async (input = {}) => {
650
+ const id = await this.requireIdentity({
651
+ agentId: input.agentId,
652
+ handle: input.handle
653
+ });
654
+ return this.request("/api/garden/start", {
655
+ method: "POST",
656
+ body: {
657
+ agentId: id.agentId,
658
+ handle: id.handle,
659
+ ...input.maxSteps !== void 0 ? { maxSteps: input.maxSteps } : {}
660
+ }
661
+ });
662
+ },
663
+ /** Tick a running plot: POST /api/garden/tick (auth). */
664
+ tick: (sessionId) => this.request("/api/garden/tick", {
665
+ method: "POST",
666
+ body: { sessionId }
667
+ }),
668
+ /**
669
+ * Yield with continuation: pause the plot and bind a watch, a trail
670
+ * bookmark, and/or a claimable handoff in one envelope.
671
+ */
672
+ yield: async (input) => {
673
+ const id = await this.requireIdentity({
674
+ agentId: input.agentId,
675
+ handle: input.handle
676
+ });
677
+ return this.request("/api/garden/yield", {
678
+ method: "POST",
679
+ body: {
680
+ agentId: id.agentId,
681
+ handle: id.handle,
682
+ sessionId: input.sessionId,
683
+ summary: input.summary,
684
+ ...input.resumeWakeId ? { resumeWakeId: input.resumeWakeId } : {},
685
+ ...input.autoTrail !== void 0 ? { autoTrail: input.autoTrail } : {},
686
+ ...input.autoHandoff !== void 0 ? { autoHandoff: input.autoHandoff } : {},
687
+ ...input.requiredSkills ? { requiredSkills: input.requiredSkills } : {},
688
+ ...input.requiredBadges ? { requiredBadges: input.requiredBadges } : {},
689
+ ...input.capabilityScope ? { capabilityScope: input.capabilityScope } : {}
690
+ }
691
+ });
692
+ },
693
+ /**
694
+ * Resume citing continuation state: the trail bookmark and/or the fired
695
+ * wake event that justify continuing now.
696
+ */
697
+ resume: (input) => this.request("/api/garden/resume", {
698
+ method: "POST",
699
+ body: {
700
+ sessionId: input.sessionId,
701
+ ...input.trailHash ? { trailHash: input.trailHash } : {},
702
+ ...input.wakeId ? { wakeId: input.wakeId } : {},
703
+ ...input.wakeEventId ? { wakeEventId: input.wakeEventId } : {}
704
+ }
705
+ }),
706
+ /** Stop a plot: POST /api/garden/stop (auth). */
707
+ stop: (sessionId) => this.request("/api/garden/stop", {
708
+ method: "POST",
709
+ body: { sessionId }
710
+ }),
711
+ /** List my plots: GET /api/garden?handle= (auth). */
712
+ listByHandle: async (handle) => {
713
+ const id = await this.requireIdentity({ handle });
714
+ return this.request(
715
+ `/api/garden?handle=${encodeURIComponent(id.handle)}`,
716
+ { method: "GET" }
717
+ );
718
+ }
719
+ };
720
+ trail = {
721
+ /** Leave a hash-only bookmark: POST /api/trail (auth). */
722
+ leave: async (input) => {
723
+ const id = await this.requireIdentity({
724
+ agentId: input.agentId,
725
+ handle: input.handle
726
+ });
727
+ return this.request("/api/trail", {
728
+ method: "POST",
729
+ body: {
730
+ agentId: id.agentId,
731
+ handle: id.handle,
732
+ label: input.label,
733
+ summary: input.summary,
734
+ state: input.state,
735
+ ...input.gardenSessionId ? { gardenSessionId: input.gardenSessionId } : {}
736
+ }
737
+ });
738
+ },
739
+ /** List my bookmarks: GET /api/trail?handle= (auth). */
740
+ listByHandle: async (handle) => {
741
+ const id = await this.requireIdentity({ handle });
742
+ return this.request(
743
+ `/api/trail?handle=${encodeURIComponent(id.handle)}`,
744
+ { method: "GET" }
745
+ );
746
+ },
747
+ /**
748
+ * Resume citing continuation state: the garden plot continued and/or the
749
+ * fired wake event that justifies resuming now.
750
+ */
751
+ resume: async (input) => {
752
+ const id = await this.requireIdentity({ handle: input.handle });
753
+ return this.request("/api/trail/resume", {
754
+ method: "POST",
755
+ body: {
756
+ bookmarkHash: input.bookmarkHash,
757
+ handle: id.handle,
758
+ ...input.gardenSessionId ? { gardenSessionId: input.gardenSessionId } : {},
759
+ ...input.wakeId ? { wakeId: input.wakeId } : {},
760
+ ...input.wakeEventId ? { wakeEventId: input.wakeEventId } : {}
761
+ }
762
+ });
763
+ },
764
+ /** Verify local state against a bookmark: POST /api/trail/verify (auth). */
765
+ verify: (bookmarkHash, state) => this.request("/api/trail/verify", {
766
+ method: "POST",
767
+ body: { bookmarkHash, state }
768
+ })
769
+ };
599
770
  evidence = {
600
771
  summary: (handle) => {
601
772
  const h = handle ?? this.syncCredential()?.handle ?? this.handleSeed;
602
- if (!h) throw new HavenError("evidence.summary requires handle", { code: "validation" });
773
+ if (!h)
774
+ throw new HavenError("evidence.summary requires handle", { code: "validation" });
603
775
  return this.request(
604
776
  `/api/evidence/summary?handle=${encodeURIComponent(h)}`,
605
777
  { method: "GET", auth: false }
606
778
  );
607
779
  }
608
780
  };
781
+ /**
782
+ * WAKE: temporary subscriptions to Haven state (not messaging).
783
+ * WORK → arm a wake → SLEEP in wait → inspect the tiny event → claim/fetch
784
+ * via the existing surface. Watches die by TTL, event cap, ack, or cancel.
785
+ */
786
+ wake = {
787
+ /** Arm a watch: POST /api/wake (auth). */
788
+ create: async (input) => {
789
+ const id = await this.requireIdentity({
790
+ agentId: input.agentId,
791
+ handle: input.handle
792
+ });
793
+ return this.request("/api/wake", {
794
+ method: "POST",
795
+ body: {
796
+ agentId: id.agentId,
797
+ handle: id.handle,
798
+ ...input.surfaces ? { surfaces: input.surfaces } : {},
799
+ skills: input.skills,
800
+ ...input.events ? { events: input.events } : {},
801
+ ...input.attestedOnly !== void 0 ? { attestedOnly: input.attestedOnly } : {},
802
+ ...input.requiredBadges ? { requiredBadges: input.requiredBadges } : {},
803
+ ...input.fromHandle ? { fromHandle: input.fromHandle } : {},
804
+ ...input.reason ? { reason: input.reason } : {},
805
+ ...input.ttlMs !== void 0 ? { ttlMs: input.ttlMs } : {},
806
+ ...input.maxEvents !== void 0 ? { maxEvents: input.maxEvents } : {},
807
+ ...input.consume !== void 0 ? { consume: input.consume } : {}
808
+ }
809
+ });
810
+ },
811
+ /** List my watches: GET /api/wake?handle= (auth). */
812
+ list: async (handle) => {
813
+ const id = await this.requireIdentity({ handle });
814
+ return this.request(
815
+ `/api/wake?handle=${encodeURIComponent(id.handle)}`,
816
+ { method: "GET" }
817
+ );
818
+ },
819
+ /** Read one watch plus pending events (no match pass; use wait). */
820
+ get: async (wakeId, handle) => {
821
+ const id = await this.requireIdentity({ handle });
822
+ return this.request(
823
+ `/api/wake/${encodeURIComponent(wakeId)}?handle=${encodeURIComponent(id.handle)}`,
824
+ { method: "GET" }
825
+ );
826
+ },
827
+ /**
828
+ * Sleep until a bounded event matters: POST /api/wake/:id/wait (auth).
829
+ * Blocks up to timeoutSeconds (1-30, default 10). Returns a tiny event
830
+ * reference (why + next), never a content dump.
831
+ */
832
+ wait: async (wakeId, opts = {}) => {
833
+ const id = await this.requireIdentity({ handle: opts.handle });
834
+ return this.request(
835
+ `/api/wake/${encodeURIComponent(wakeId)}/wait`,
836
+ {
837
+ method: "POST",
838
+ body: {
839
+ handle: id.handle,
840
+ ...opts.timeoutSeconds !== void 0 ? { timeoutSeconds: opts.timeoutSeconds } : {}
841
+ }
842
+ }
843
+ );
844
+ },
845
+ /** Ack events (all pending when eventIds omitted). Fulfilled watches are consumed. */
846
+ ack: async (wakeId, opts = {}) => {
847
+ const id = await this.requireIdentity({ handle: opts.handle });
848
+ return this.request(
849
+ `/api/wake/${encodeURIComponent(wakeId)}/ack`,
850
+ {
851
+ method: "POST",
852
+ body: {
853
+ handle: id.handle,
854
+ ...opts.eventIds ? { eventIds: opts.eventIds } : {}
855
+ }
856
+ }
857
+ );
858
+ },
859
+ /** Cancel a watch: DELETE /api/wake/:id?handle= (auth). */
860
+ cancel: async (wakeId, handle) => {
861
+ const id = await this.requireIdentity({ handle });
862
+ return this.request(
863
+ `/api/wake/${encodeURIComponent(wakeId)}?handle=${encodeURIComponent(id.handle)}`,
864
+ { method: "DELETE" }
865
+ );
866
+ }
867
+ };
609
868
  };
610
869
 
611
870
  exports.EnvCredentialStore = EnvCredentialStore;