@@ -15,6 +15,53 @@ export interface CommonlyClientConfig {
1515 instanceId ?: string ;
1616}
1717
18+ // ADR-003's typed memory envelope. `cycles` has a separate append-only write
19+ // shape, so it is deliberately excluded from MemorySectionName below.
20+ export type MemoryVisibility = "private" | "pod" | "public" ;
21+
22+ export interface MemorySection {
23+ content : string ;
24+ visibility ?: MemoryVisibility ;
25+ byteSize ?: number ;
26+ updatedAt ?: string ;
27+ }
28+
29+ export interface DailySection {
30+ date : string ;
31+ content : string ;
32+ visibility ?: MemoryVisibility ;
33+ }
34+
35+ export interface RelationshipNote {
36+ otherInstanceId : string ;
37+ notes ?: string ;
38+ visibility ?: MemoryVisibility ;
39+ updatedAt ?: string ;
40+ }
41+
42+ export interface CycleEntry {
43+ content : string ;
44+ ts ?: string ;
45+ podId ?: string ;
46+ }
47+
48+ export interface AgentMemorySections {
49+ soul ?: MemorySection ;
50+ long_term ?: MemorySection ;
51+ daily ?: DailySection [ ] ;
52+ dedup_state ?: MemorySection ;
53+ relationships ?: RelationshipNote [ ] ;
54+ shared ?: MemorySection ;
55+ runtime_meta ?: MemorySection ;
56+ cycles ?: { entries ?: CycleEntry [ ] ; visibility ?: MemoryVisibility } ;
57+ }
58+
59+ export type MemorySectionName = Exclude < keyof AgentMemorySections , "cycles" > ;
60+
61+ export type AgentMemorySyncSections = Omit < AgentMemorySections , "cycles" > & {
62+ cycles ?: { append : CycleEntry } ;
63+ } ;
64+
1865export interface PodContext {
1966 pod ?: {
2067 name : string ;
@@ -196,14 +243,11 @@ export class CommonlyClient {
196243 form . append ( "podId" , podId ) ;
197244
198245 // NOTE: do not set Content-Type — fetch derives the multipart boundary.
199- const res = await fetch (
200- `${ this . config . baseUrl } /api/agents/runtime/pods/${ podId } /uploads` ,
201- {
202- method : "POST" ,
203- headers : { Authorization : `Bearer ${ token } ` } ,
204- body : form ,
205- } ,
206- ) ;
246+ const res = await fetch ( `${ this . config . baseUrl } /api/agents/runtime/pods/${ podId } /uploads` , {
247+ method : "POST" ,
248+ headers : { Authorization : `Bearer ${ token } ` } ,
249+ body : form ,
250+ } ) ;
207251 if ( ! res . ok ) {
208252 let detail = `${ res . status } ` ;
209253 try {
@@ -487,9 +531,15 @@ export class CommonlyClient {
487531 }
488532
489533 /**
490- * Read this agent's personal MEMORY.md (stored in backend, persistent across sessions)
534+ * Read this agent's personal memory. The v1 content blob remains for
535+ * compatibility; new callers should use the typed sections envelope.
491536 */
492- async readAgentMemory ( ) : Promise < { content : string } > {
537+ async readAgentMemory ( ) : Promise < {
538+ content : string ;
539+ sections ?: AgentMemorySections ;
540+ sourceRuntime ?: string ;
541+ schemaVersion ?: number ;
542+ } > {
493543 const res = await fetch ( `${ this . config . baseUrl } /api/agents/runtime/memory` , {
494544 headers : this . runtimeHeaders ,
495545 } ) ;
@@ -509,6 +559,58 @@ export class CommonlyClient {
509559 if ( ! res . ok ) throw new Error ( `Failed to write agent memory: ${ res . status } ` ) ;
510560 }
511561
562+ /**
563+ * Patch or replace typed agent-memory sections. Cycles are only accepted as
564+ * an append payload, matching the kernel's append-only contract.
565+ */
566+ async syncAgentMemory (
567+ sections : AgentMemorySyncSections ,
568+ options : { mode : "full" | "patch" ; sourceRuntime ?: string } ,
569+ ) : Promise < {
570+ ok : true ;
571+ deduped ?: boolean ;
572+ schemaVersion ?: number ;
573+ version ?: number ;
574+ cyclesAppended ?: boolean ;
575+ truncated ?: boolean ;
576+ storedChars ?: number ;
577+ submittedChars ?: number ;
578+ evicted ?: boolean ;
579+ retainedEntries ?: number ;
580+ entryCap ?: number ;
581+ } > {
582+ const res = await fetch ( `${ this . config . baseUrl } /api/agents/runtime/memory/sync` , {
583+ method : "POST" ,
584+ headers : this . runtimeHeaders ,
585+ body : JSON . stringify ( {
586+ sections,
587+ mode : options . mode ,
588+ ...( options . sourceRuntime !== undefined ? { sourceRuntime : options . sourceRuntime } : { } ) ,
589+ } ) ,
590+ } ) ;
591+ if ( ! res . ok ) {
592+ const text = await res . text ( ) . catch ( ( ) => "" ) ;
593+ throw new Error ( `Failed to sync agent memory: ${ res . status } ${ text } ` ) ;
594+ }
595+ return res . json ( ) ;
596+ }
597+
598+ /**
599+ * Fetch an attachment with the agent runtime token. The uploads route uses
600+ * the same pod-membership ACL as agent writes; callers receive bytes only
601+ * after that authorization succeeds.
602+ */
603+ async readAttachment ( fileName : string ) : Promise < Buffer > {
604+ const res = await fetch ( `${ this . config . baseUrl } /api/uploads/${ encodeURIComponent ( fileName ) } ` , {
605+ headers : this . runtimeHeaders ,
606+ } ) ;
607+ if ( ! res . ok ) {
608+ const text = await res . text ( ) . catch ( ( ) => "" ) ;
609+ throw new Error ( `Failed to read attachment: ${ res . status } ${ text } ` ) ;
610+ }
611+ return Buffer . from ( await res . arrayBuffer ( ) ) ;
612+ }
613+
512614 /**
513615 * Self-install this agent into an agent-owned pod
514616 */
@@ -528,6 +630,37 @@ export class CommonlyClient {
528630 return res . json ( ) ;
529631 }
530632
633+ /**
634+ * Open or retrieve a 1:1 agent DM. The server enforces that both agents
635+ * already share a pod before allowing the private room.
636+ */
637+ async openAgentDm (
638+ target : { agentName : string ; instanceId ?: string } ,
639+ originPodId ?: string ,
640+ ) : Promise < {
641+ room : { _id : string ; name ?: string ; type ?: string ; members ?: unknown [ ] } ;
642+ autoJoined : boolean ;
643+ } > {
644+ const body : Record < string , unknown > = {
645+ target : {
646+ agentName : target . agentName ,
647+ ...( target . instanceId ? { instanceId : target . instanceId } : { } ) ,
648+ } ,
649+ } ;
650+ if ( originPodId ) body . originPodId = originPodId ;
651+
652+ const res = await fetch ( `${ this . config . baseUrl } /api/agents/runtime/agent-dm` , {
653+ method : "POST" ,
654+ headers : this . runtimeHeaders ,
655+ body : JSON . stringify ( body ) ,
656+ } ) ;
657+ if ( ! res . ok ) {
658+ const text = await res . text ( ) . catch ( ( ) => "" ) ;
659+ throw new Error ( `Failed to open agent DM: ${ res . status } ${ text } ` ) ;
660+ }
661+ return res . json ( ) ;
662+ }
663+
531664 /**
532665 * List tasks for a pod
533666 */
0 commit comments