@covia/covia-sdk 1.17.0 → 1.18.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.
package/dist/index.d.mts CHANGED
@@ -24,6 +24,13 @@ declare class Agent {
24
24
  listSessions(options?: AgentSessionListOptions): Promise<AgentSessionPage>;
25
25
  /** Read one session through the job-free workspace Values API. */
26
26
  getSession(sessionId: string): Promise<AgentSession>;
27
+ /**
28
+ * One session's transcript — its turns in order, with compacted segments
29
+ * expanded back into the turns they archived.
30
+ *
31
+ * Shorthand for `getSession(sessionId)` when only the conversation is wanted.
32
+ */
33
+ transcript(sessionId: string): Promise<AgentSessionMessage[]>;
27
34
  /** Stream this agent's run-loop events. See `AgentManager.events`. */
28
35
  events(options?: {
29
36
  sessionId?: string;
@@ -369,6 +376,9 @@ interface AgentManagerVenue extends VenueRequestContext {
369
376
  workspace: {
370
377
  read(path: string, maxSize?: number): Promise<WorkspaceReadResult>;
371
378
  slice(path: string, offset?: number, limit?: number): Promise<WorkspaceSliceResult>;
379
+ count(path: string, opts?: {
380
+ depth?: number;
381
+ }): Promise<WorkspaceCountResult>;
372
382
  };
373
383
  }
374
384
  declare class AgentManager {
@@ -446,6 +456,8 @@ declare class AgentManager {
446
456
  * available on each record's `value` while common runtime fields are typed.
447
457
  */
448
458
  listSessions(agentId: string, options?: AgentSessionListOptions): Promise<AgentSessionPage>;
459
+ /** Shape a `{key, value}` session slice into a page. */
460
+ private _sessionPage;
449
461
  /** Read one agent session directly from the workspace Values surface. */
450
462
  getSession(agentId: string, sessionId: string): Promise<AgentSession>;
451
463
  private _readLargeSession;
@@ -455,11 +467,39 @@ declare class AgentManager {
455
467
  failTask(error: string): Promise<AgentFailTaskResult>;
456
468
  }
457
469
 
458
- type JobManagerVenue = VenueRequestContext;
470
+ interface JobManagerVenue extends VenueRequestContext {
471
+ workspace: {
472
+ count(path: string, opts?: {
473
+ depth?: number;
474
+ }): Promise<WorkspaceCountResult>;
475
+ slice(path: string, offset?: number, limit?: number): Promise<WorkspaceSliceResult>;
476
+ };
477
+ }
459
478
  declare class JobManager {
460
479
  private venue;
461
480
  constructor(venue: JobManagerVenue);
462
481
  list(): Promise<string[]>;
482
+ /**
483
+ * A page of job history: `JobMetadata` rows, newest-first by default, with an
484
+ * authoritative total.
485
+ *
486
+ * `list()` answers "which jobs exist" as ids; a history view needs a bounded
487
+ * window of *records* and a count to page against. This reads the job index
488
+ * directly, so each row arrives with its metadata already attached — the
489
+ * alternative, listing ids and then fetching each one, is an N+1 fan-out per
490
+ * page. Reads stay job-free (covia#177): paging job history must not itself
491
+ * write a job record per page.
492
+ *
493
+ * The total is read before a `desc` window can be placed, so the index may
494
+ * grow in between; the page self-corrects once against the count the slice
495
+ * itself reports, which is authoritative for the rows returned with it.
496
+ */
497
+ history(options?: JobHistoryOptions): Promise<JobHistoryPage>;
498
+ /**
499
+ * Read index ranks `[start, end)` in cap-aware chunks, halving on an oversize
500
+ * response and skipping a record that is individually oversize.
501
+ */
502
+ private _readJobRange;
463
503
  get(jobId: string): Promise<Job>;
464
504
  cancel(jobId: string): Promise<JobMetadata>;
465
505
  delete(jobId: string): Promise<void>;
@@ -610,6 +650,7 @@ interface WorkspaceManagerVenue extends VenueRequestContext {
610
650
  declare class WorkspaceManager {
611
651
  private venue;
612
652
  private valuesSupported;
653
+ private scopeExpansionSupported;
613
654
  constructor(venue: WorkspaceManagerVenue);
614
655
  /** GET a job-free `/api/v1/values/{op}` read; omit undefined params. */
615
656
  private _values;
@@ -624,6 +665,34 @@ declare class WorkspaceManager {
624
665
  private unsupported;
625
666
  /** A job-free Values read that rejects when the endpoint is unavailable. */
626
667
  private valuesRead;
668
+ /**
669
+ * A job-free Values read of an execution-scoped path, preferring the venue's
670
+ * own selector expansion and falling back to the client-side one.
671
+ *
672
+ * The venue is preferred because it expands *before* the capability check, so
673
+ * authorisation applies to the same canonical resource that is ultimately
674
+ * read — a caller cannot substitute a sibling or foreign agent's scratch.
675
+ * The fallback reaches the same resource but re-derives the layout here.
676
+ */
677
+ private scopedRead;
678
+ /**
679
+ * Bind an execution scope, giving job-free reads of the `t/`, `n/` and `c/`
680
+ * scratch shorthands that a bare GET cannot otherwise resolve (covia#177
681
+ * forbids minting a Job for a read; the shorthands need context a GET lacks).
682
+ *
683
+ * ```ts
684
+ * const scratch = venue.workspace.scoped({ agent: 'alice', task: taskId });
685
+ * await scratch.read('t/snapshot');
686
+ * await scratch.list('t/');
687
+ * ```
688
+ *
689
+ * Ordinary paths still work through the handle, so a caller rendering a task
690
+ * inspector can use one reader for both scratch and workspace. Capability
691
+ * behaviour is unchanged: the caller needs read caps on the agent's subtree.
692
+ */
693
+ scoped(scope: ExecutionScope): ScopedWorkspace;
694
+ /** @internal — the scoped read used by {@link ScopedWorkspace}. */
695
+ _scopedRead<T>(op: string, params: Record<string, string | number | boolean | undefined>, scope: ExecutionScope): Promise<T>;
627
696
  read(path: string, maxSize?: number, ucans?: string[]): Promise<WorkspaceReadResult>;
628
697
  list(path?: string, limit?: number, offset?: number, ucans?: string[]): Promise<WorkspaceListResult>;
629
698
  slice(path: string, offset?: number, limit?: number, ucans?: string[]): Promise<WorkspaceSliceResult>;
@@ -658,6 +727,35 @@ declare class WorkspaceManager {
658
727
  append(path: string, value: unknown, ucans?: string[]): Promise<WorkspaceAppendResult>;
659
728
  copy(from: string, to: string, ucans?: string[]): Promise<WorkspaceCopyResult>;
660
729
  }
730
+ /**
731
+ * A {@link WorkspaceManager} bound to one execution scope, so `t/`, `n/` and
732
+ * `c/` scratch resolve without the caller hand-building the physical paths
733
+ * (`g/<agent>/sessions/<sid>/c/...` and friends). Reads only — scoped scratch
734
+ * is written from inside the execution that owns it, where the shorthands
735
+ * already resolve from the request's own context.
736
+ *
737
+ * Obtained from {@link WorkspaceManager.scoped}; every method mirrors the
738
+ * job-free read of the same name.
739
+ */
740
+ declare class ScopedWorkspace {
741
+ private workspace;
742
+ /** The scope every read on this handle resolves against. */
743
+ readonly scope: ExecutionScope;
744
+ constructor(workspace: WorkspaceManager,
745
+ /** The scope every read on this handle resolves against. */
746
+ scope: ExecutionScope);
747
+ read(path: string, maxSize?: number): Promise<WorkspaceReadResult>;
748
+ list(path: string, limit?: number, offset?: number): Promise<WorkspaceListResult>;
749
+ slice(path: string, offset?: number, limit?: number): Promise<WorkspaceSliceResult>;
750
+ inspect(path: string, budget?: number, compact?: boolean): Promise<WorkspaceInspectResult>;
751
+ count(path: string, opts?: {
752
+ depth?: number;
753
+ }): Promise<WorkspaceCountResult>;
754
+ aggregate(path: string, opts?: {
755
+ depth?: number;
756
+ groupBy?: string;
757
+ }): Promise<WorkspaceAggregateResult>;
758
+ }
661
759
 
662
760
  interface UCANManagerVenue {
663
761
  operations: OperationRunner;
@@ -1149,6 +1247,23 @@ interface OperationPayload {
1149
1247
  interface ContentHashResult {
1150
1248
  hash: string;
1151
1249
  }
1250
+ /** Paging/ordering for {@link JobManager.history}. */
1251
+ interface JobHistoryOptions {
1252
+ /** Rows to skip from the ordered start (default 0). */
1253
+ offset?: number;
1254
+ /** Maximum rows to return (default 50). */
1255
+ limit?: number;
1256
+ /** `desc` (default) is newest-first, the order a history view wants. */
1257
+ order?: 'asc' | 'desc';
1258
+ }
1259
+ /** One page of job history: the rows plus the window they were drawn from. */
1260
+ interface JobHistoryPage {
1261
+ items: JobMetadata[];
1262
+ /** Authoritative total at the moment the page was read. */
1263
+ total: number;
1264
+ offset: number;
1265
+ limit: number;
1266
+ }
1152
1267
  interface JobMetadata {
1153
1268
  id?: string;
1154
1269
  name?: string;
@@ -1438,17 +1553,78 @@ interface AgentSessionMetadata {
1438
1553
  turnCount?: number;
1439
1554
  [key: string]: unknown;
1440
1555
  }
1556
+ /** The four roles a stored conversation turn may carry. */
1557
+ type AgentSessionRole = 'system' | 'user' | 'assistant' | 'tool';
1558
+ /**
1559
+ * One persisted conversation turn (AGENT_CONTEXT.md §1.1 `Turn`).
1560
+ *
1561
+ * Most turns carry text in `content`; a tool result may instead carry its typed
1562
+ * map or vector in `structuredContent`. Provider-specific continuation state
1563
+ * (`providerState`) is deliberately absent — it is opaque, is omitted from
1564
+ * retrospective session projections, and is not part of a readable transcript.
1565
+ */
1566
+ interface AgentSessionMessage {
1567
+ role: AgentSessionRole;
1568
+ content?: unknown;
1569
+ /** A typed tool result, in place of textual `content`. */
1570
+ structuredContent?: unknown;
1571
+ /** Present on an assistant turn that called tools. */
1572
+ toolCalls?: unknown[];
1573
+ /** A `tool` turn's matching call id. */
1574
+ id?: string;
1575
+ name?: string;
1576
+ isError?: boolean;
1577
+ ts?: number;
1578
+ source?: string;
1579
+ caller?: string;
1580
+ /** The job that delivered this turn — links it back to the calling job. */
1581
+ jobId?: string;
1582
+ tokens?: number;
1583
+ }
1584
+ /**
1585
+ * An archived run of turns that compaction replaced with a summary
1586
+ * (AGENT_CONTEXT.md §1.1 `CompactedSegment`). `items` is the exact recursively
1587
+ * archived vector, so no history is lost — {@link AgentSession.conversation}
1588
+ * expands it back into the transcript.
1589
+ */
1590
+ interface AgentCompactedSegment {
1591
+ summary: string;
1592
+ /** How many turns the summary stands in for. */
1593
+ turns?: number;
1594
+ items: unknown[];
1595
+ }
1441
1596
  /** A session record read directly from `g/<agentId>/sessions`. */
1442
1597
  interface AgentSession {
1443
1598
  id: string;
1444
1599
  metadata: AgentSessionMetadata;
1445
1600
  pending: unknown[];
1601
+ /**
1602
+ * Raw goal-tree frames, newest last. `frames[0]` is the root frame and a flat
1603
+ * `llmagent` session has only that one. Kept verbatim for callers that need
1604
+ * frame-level state; for the readable transcript use {@link conversation}.
1605
+ */
1446
1606
  frames: unknown[];
1607
+ /**
1608
+ * The session transcript: every frame's turns, in order, with compacted
1609
+ * segments expanded back into the turns they archived.
1610
+ *
1611
+ * This is the field a chat UI renders. Assembling it from `frames` means
1612
+ * knowing that turns live at `frames[].conversation`, that an entry there may
1613
+ * be an archived segment rather than a turn, and that segments nest — layout
1614
+ * knowledge that belongs here rather than in every consumer.
1615
+ */
1616
+ conversation: AgentSessionMessage[];
1447
1617
  wakeTime?: number;
1448
1618
  }
1449
1619
  interface AgentSessionListOptions {
1450
1620
  offset?: number;
1451
1621
  limit?: number;
1622
+ /**
1623
+ * `desc` is newest-first. Session ids are timestamp-prefixed, so the index is
1624
+ * already chronological and the window is taken from its end — the order
1625
+ * holds across pages, unlike sorting one page after the fact.
1626
+ */
1627
+ order?: 'asc' | 'desc';
1452
1628
  }
1453
1629
  interface AgentSessionPage {
1454
1630
  items: AgentSession[];
@@ -1640,6 +1816,33 @@ interface WorkspaceAppendResult {
1640
1816
  /** 0.3.0: true iff a new parent path was built. */
1641
1817
  pathCreated?: boolean;
1642
1818
  }
1819
+ /**
1820
+ * The execution context a `t/`, `n/` or `c/` scratch read resolves against.
1821
+ *
1822
+ * A GET carries no execution context of its own, so the venue cannot know which
1823
+ * job, agent or session a bare shorthand means. These selectors supply it
1824
+ * explicitly (covia#230) and are the only way to read scoped scratch job-free.
1825
+ *
1826
+ * Each namespace consumes a different part of the scope, and the venue rejects
1827
+ * a selector its namespace does not use:
1828
+ *
1829
+ * | Path | Needs | Resolves to |
1830
+ * |------|------------------|----------------------------------------------|
1831
+ * | `n/` | `agent` | `g/<agent>/n/...` |
1832
+ * | `c/` | `agent`+`session`| `g/<agent>/sessions/<session>/c/...` |
1833
+ * | `t/` | `agent`+`task` | `j/<task>/temp/...` |
1834
+ *
1835
+ * A bound scope may therefore carry all three; each read sends only what its
1836
+ * own namespace needs.
1837
+ */
1838
+ interface ExecutionScope {
1839
+ /** Agent id (`alice`) or a full agent DID. Required for every scoped read. */
1840
+ agent: string;
1841
+ /** Task id — the `agent:request` Job whose record holds `t/` scratch. */
1842
+ task?: string;
1843
+ /** Session id, for `c/` session scratch. */
1844
+ session?: string;
1845
+ }
1643
1846
  interface WorkspaceListInput {
1644
1847
  path?: string;
1645
1848
  limit?: number;
@@ -2282,4 +2485,4 @@ declare function grant(ownerPrivateKey: Uint8Array, audienceDID: string, withRes
2282
2485
  */
2283
2486
  declare function relayDelegation(privateKey: Uint8Array, venueDID: string, lifetimeSeconds: number, caps?: UCANCapability[]): string;
2284
2487
 
2285
- export { type A2AAuthRef, type A2AImportAgentInput, type A2AImportAgentResult, A2AManager, type A2AMessage, type A2APart, type A2ATask, type AdapterInfo, AdapterManager, type AdaptersResult, Agent, type AgentCancelTaskInput, type AgentCard, type AgentChatInput, type AgentChatResult, type AgentCompleteTaskResult, type AgentContextInput, type AgentCreateInput, type AgentCreateResult, type AgentCycleEndEvent, type AgentCycleStartEvent, type AgentDeleteInput, type AgentDeleteResult, type AgentEvent, type AgentEventEnvelope, type AgentFailTaskResult, type AgentForkInput, type AgentForkResult, type AgentInferenceEndEvent, type AgentInferenceStartEvent, type AgentInfoResult, type AgentListInput, type AgentListResult, AgentManager, type AgentMessageInput, type AgentMessageResult, type AgentRenameSessionResult, type AgentRequestInput, type AgentRequestResult, type AgentResumeInput, type AgentRunEndEvent, type AgentRunStartEvent, type AgentSession, type AgentSessionListOptions, type AgentSessionMetadata, AgentSessionNotFoundError, type AgentSessionPage, AgentStatus, type AgentStatusEvent, type AgentSuspendResult, type AgentToolResultEvent, type AgentToolStartEvent, type AgentTriggerInput, type AgentTriggerResult, type AgentUpdateInput, Asset, type AssetID, type AssetList, type AssetListItem, type AssetListOptions, AssetManager, type AssetMetadata, type AssetMetadataStore, AssetNotFoundError, type AssetPinResult, type AssetSummary, Auth, type AuthenticationKeyEntry, type AuthenticationKeysMap, type AuthenticationKeysResult, type AuthenticatorRevokeResult, BasicAuth, BearerAuth, ChatSession, type ConnectionAttempt, type ContentDetails, type ContentHashResult, CoviaConnectionError, type CoviaConnectionErrorOptions, CoviaError, CoviaTimeoutError, type Credentials, CredentialsHTTP, type DIDDocument, type DIDURL, type DLFSDrivesResult, type DLFSEntry, type DLFSListResult, DLFSManager, DataAsset, Ed25519Auth, type ExpandedAssetList, type FunctionInfo, type FunctionsResult, Grid, GridError, type GroupCount, type InvokeOptions, type InvokePayload, Job, JobFailedError, JobManager, type JobMetadata, JobNotFoundError, JobStatus, KeyPairAuth, type MCPDiscovery, type MemoryChange, type MemoryEntry, MemoryManager, type MyAssetList, Namespace, NoAuth, NotFoundError, Operation, type OperationDetails, type OperationInfo, OperationManager, type OperationPayload, type OperationRunner, RateLimitError, RunStatus, type SSEEvent, type SecretExtractInput, type SecretExtractResult, SecretManager, type SecretSetInput, type SecretSetResult, type Skill, type SkillFacet, SkillManager, type SkillMetadata, type StatsData, type StatusData, type UCANAttenuation, type UCANCapability, type UCANIssueInput, type UCANIssueResult, UCANManager, type UCANVerifyResult, UnsupportedVenueFeatureError, type UserInfo, type UserListResult, UserManager, type UserSummary, VENUE_RELAY, Venue, type VenueConstructor, type VenueData, VenueIdentityChangedError, type VenueInterface, type VenueOptions, type WorkspaceAggregateResult, type WorkspaceAppendInput, type WorkspaceAppendResult, type WorkspaceCopyResult, type WorkspaceCountResult, type WorkspaceDeleteInput, type WorkspaceDeleteResult, type WorkspaceInspectInput, type WorkspaceInspectResult, type WorkspaceListInput, type WorkspaceListResult, WorkspaceManager, type WorkspaceReadInput, type WorkspaceReadResult, type WorkspaceSliceInput, type WorkspaceSliceResult, type WorkspaceWriteInput, type WorkspaceWriteResult, assetHash, createSSEEvent, createUCANJWT, decodePublicKey, didFor, didFromPublicKey, didMethod, didUrl, encodePublicKey, fetchStreamWithError, fetchWithError, generateKeyPair, getAssetIdFromPath, getAssetIdFromVenueId, getAssetMetadataStore, getParsedAssetId, grant, hexToPrivateKey, identityToken, isDid, isJobComplete, isJobFinished, isJobPaused, logger, parseDidUrl, parseRetryAfterMs, parseSSEStream, privateKeyToHex, relayDelegation, retryDelayMs, setAssetMetadataStore, venueBaseUrlCandidates };
2488
+ export { type A2AAuthRef, type A2AImportAgentInput, type A2AImportAgentResult, A2AManager, type A2AMessage, type A2APart, type A2ATask, type AdapterInfo, AdapterManager, type AdaptersResult, Agent, type AgentCancelTaskInput, type AgentCard, type AgentChatInput, type AgentChatResult, type AgentCompactedSegment, type AgentCompleteTaskResult, type AgentContextInput, type AgentCreateInput, type AgentCreateResult, type AgentCycleEndEvent, type AgentCycleStartEvent, type AgentDeleteInput, type AgentDeleteResult, type AgentEvent, type AgentEventEnvelope, type AgentFailTaskResult, type AgentForkInput, type AgentForkResult, type AgentInferenceEndEvent, type AgentInferenceStartEvent, type AgentInfoResult, type AgentListInput, type AgentListResult, AgentManager, type AgentMessageInput, type AgentMessageResult, type AgentRenameSessionResult, type AgentRequestInput, type AgentRequestResult, type AgentResumeInput, type AgentRunEndEvent, type AgentRunStartEvent, type AgentSession, type AgentSessionListOptions, type AgentSessionMessage, type AgentSessionMetadata, AgentSessionNotFoundError, type AgentSessionPage, type AgentSessionRole, AgentStatus, type AgentStatusEvent, type AgentSuspendResult, type AgentToolResultEvent, type AgentToolStartEvent, type AgentTriggerInput, type AgentTriggerResult, type AgentUpdateInput, Asset, type AssetID, type AssetList, type AssetListItem, type AssetListOptions, AssetManager, type AssetMetadata, type AssetMetadataStore, AssetNotFoundError, type AssetPinResult, type AssetSummary, Auth, type AuthenticationKeyEntry, type AuthenticationKeysMap, type AuthenticationKeysResult, type AuthenticatorRevokeResult, BasicAuth, BearerAuth, ChatSession, type ConnectionAttempt, type ContentDetails, type ContentHashResult, CoviaConnectionError, type CoviaConnectionErrorOptions, CoviaError, CoviaTimeoutError, type Credentials, CredentialsHTTP, type DIDDocument, type DIDURL, type DLFSDrivesResult, type DLFSEntry, type DLFSListResult, DLFSManager, DataAsset, Ed25519Auth, type ExecutionScope, type ExpandedAssetList, type FunctionInfo, type FunctionsResult, Grid, GridError, type GroupCount, type InvokeOptions, type InvokePayload, Job, JobFailedError, type JobHistoryOptions, type JobHistoryPage, JobManager, type JobMetadata, JobNotFoundError, JobStatus, KeyPairAuth, type MCPDiscovery, type MemoryChange, type MemoryEntry, MemoryManager, type MyAssetList, Namespace, NoAuth, NotFoundError, Operation, type OperationDetails, type OperationInfo, OperationManager, type OperationPayload, type OperationRunner, RateLimitError, RunStatus, type SSEEvent, ScopedWorkspace, type SecretExtractInput, type SecretExtractResult, SecretManager, type SecretSetInput, type SecretSetResult, type Skill, type SkillFacet, SkillManager, type SkillMetadata, type StatsData, type StatusData, type UCANAttenuation, type UCANCapability, type UCANIssueInput, type UCANIssueResult, UCANManager, type UCANVerifyResult, UnsupportedVenueFeatureError, type UserInfo, type UserListResult, UserManager, type UserSummary, VENUE_RELAY, Venue, type VenueConstructor, type VenueData, VenueIdentityChangedError, type VenueInterface, type VenueOptions, type WorkspaceAggregateResult, type WorkspaceAppendInput, type WorkspaceAppendResult, type WorkspaceCopyResult, type WorkspaceCountResult, type WorkspaceDeleteInput, type WorkspaceDeleteResult, type WorkspaceInspectInput, type WorkspaceInspectResult, type WorkspaceListInput, type WorkspaceListResult, WorkspaceManager, type WorkspaceReadInput, type WorkspaceReadResult, type WorkspaceSliceInput, type WorkspaceSliceResult, type WorkspaceWriteInput, type WorkspaceWriteResult, assetHash, createSSEEvent, createUCANJWT, decodePublicKey, didFor, didFromPublicKey, didMethod, didUrl, encodePublicKey, fetchStreamWithError, fetchWithError, generateKeyPair, getAssetIdFromPath, getAssetIdFromVenueId, getAssetMetadataStore, getParsedAssetId, grant, hexToPrivateKey, identityToken, isDid, isJobComplete, isJobFinished, isJobPaused, logger, parseDidUrl, parseRetryAfterMs, parseSSEStream, privateKeyToHex, relayDelegation, retryDelayMs, setAssetMetadataStore, venueBaseUrlCandidates };
package/dist/index.d.ts CHANGED
@@ -24,6 +24,13 @@ declare class Agent {
24
24
  listSessions(options?: AgentSessionListOptions): Promise<AgentSessionPage>;
25
25
  /** Read one session through the job-free workspace Values API. */
26
26
  getSession(sessionId: string): Promise<AgentSession>;
27
+ /**
28
+ * One session's transcript — its turns in order, with compacted segments
29
+ * expanded back into the turns they archived.
30
+ *
31
+ * Shorthand for `getSession(sessionId)` when only the conversation is wanted.
32
+ */
33
+ transcript(sessionId: string): Promise<AgentSessionMessage[]>;
27
34
  /** Stream this agent's run-loop events. See `AgentManager.events`. */
28
35
  events(options?: {
29
36
  sessionId?: string;
@@ -369,6 +376,9 @@ interface AgentManagerVenue extends VenueRequestContext {
369
376
  workspace: {
370
377
  read(path: string, maxSize?: number): Promise<WorkspaceReadResult>;
371
378
  slice(path: string, offset?: number, limit?: number): Promise<WorkspaceSliceResult>;
379
+ count(path: string, opts?: {
380
+ depth?: number;
381
+ }): Promise<WorkspaceCountResult>;
372
382
  };
373
383
  }
374
384
  declare class AgentManager {
@@ -446,6 +456,8 @@ declare class AgentManager {
446
456
  * available on each record's `value` while common runtime fields are typed.
447
457
  */
448
458
  listSessions(agentId: string, options?: AgentSessionListOptions): Promise<AgentSessionPage>;
459
+ /** Shape a `{key, value}` session slice into a page. */
460
+ private _sessionPage;
449
461
  /** Read one agent session directly from the workspace Values surface. */
450
462
  getSession(agentId: string, sessionId: string): Promise<AgentSession>;
451
463
  private _readLargeSession;
@@ -455,11 +467,39 @@ declare class AgentManager {
455
467
  failTask(error: string): Promise<AgentFailTaskResult>;
456
468
  }
457
469
 
458
- type JobManagerVenue = VenueRequestContext;
470
+ interface JobManagerVenue extends VenueRequestContext {
471
+ workspace: {
472
+ count(path: string, opts?: {
473
+ depth?: number;
474
+ }): Promise<WorkspaceCountResult>;
475
+ slice(path: string, offset?: number, limit?: number): Promise<WorkspaceSliceResult>;
476
+ };
477
+ }
459
478
  declare class JobManager {
460
479
  private venue;
461
480
  constructor(venue: JobManagerVenue);
462
481
  list(): Promise<string[]>;
482
+ /**
483
+ * A page of job history: `JobMetadata` rows, newest-first by default, with an
484
+ * authoritative total.
485
+ *
486
+ * `list()` answers "which jobs exist" as ids; a history view needs a bounded
487
+ * window of *records* and a count to page against. This reads the job index
488
+ * directly, so each row arrives with its metadata already attached — the
489
+ * alternative, listing ids and then fetching each one, is an N+1 fan-out per
490
+ * page. Reads stay job-free (covia#177): paging job history must not itself
491
+ * write a job record per page.
492
+ *
493
+ * The total is read before a `desc` window can be placed, so the index may
494
+ * grow in between; the page self-corrects once against the count the slice
495
+ * itself reports, which is authoritative for the rows returned with it.
496
+ */
497
+ history(options?: JobHistoryOptions): Promise<JobHistoryPage>;
498
+ /**
499
+ * Read index ranks `[start, end)` in cap-aware chunks, halving on an oversize
500
+ * response and skipping a record that is individually oversize.
501
+ */
502
+ private _readJobRange;
463
503
  get(jobId: string): Promise<Job>;
464
504
  cancel(jobId: string): Promise<JobMetadata>;
465
505
  delete(jobId: string): Promise<void>;
@@ -610,6 +650,7 @@ interface WorkspaceManagerVenue extends VenueRequestContext {
610
650
  declare class WorkspaceManager {
611
651
  private venue;
612
652
  private valuesSupported;
653
+ private scopeExpansionSupported;
613
654
  constructor(venue: WorkspaceManagerVenue);
614
655
  /** GET a job-free `/api/v1/values/{op}` read; omit undefined params. */
615
656
  private _values;
@@ -624,6 +665,34 @@ declare class WorkspaceManager {
624
665
  private unsupported;
625
666
  /** A job-free Values read that rejects when the endpoint is unavailable. */
626
667
  private valuesRead;
668
+ /**
669
+ * A job-free Values read of an execution-scoped path, preferring the venue's
670
+ * own selector expansion and falling back to the client-side one.
671
+ *
672
+ * The venue is preferred because it expands *before* the capability check, so
673
+ * authorisation applies to the same canonical resource that is ultimately
674
+ * read — a caller cannot substitute a sibling or foreign agent's scratch.
675
+ * The fallback reaches the same resource but re-derives the layout here.
676
+ */
677
+ private scopedRead;
678
+ /**
679
+ * Bind an execution scope, giving job-free reads of the `t/`, `n/` and `c/`
680
+ * scratch shorthands that a bare GET cannot otherwise resolve (covia#177
681
+ * forbids minting a Job for a read; the shorthands need context a GET lacks).
682
+ *
683
+ * ```ts
684
+ * const scratch = venue.workspace.scoped({ agent: 'alice', task: taskId });
685
+ * await scratch.read('t/snapshot');
686
+ * await scratch.list('t/');
687
+ * ```
688
+ *
689
+ * Ordinary paths still work through the handle, so a caller rendering a task
690
+ * inspector can use one reader for both scratch and workspace. Capability
691
+ * behaviour is unchanged: the caller needs read caps on the agent's subtree.
692
+ */
693
+ scoped(scope: ExecutionScope): ScopedWorkspace;
694
+ /** @internal — the scoped read used by {@link ScopedWorkspace}. */
695
+ _scopedRead<T>(op: string, params: Record<string, string | number | boolean | undefined>, scope: ExecutionScope): Promise<T>;
627
696
  read(path: string, maxSize?: number, ucans?: string[]): Promise<WorkspaceReadResult>;
628
697
  list(path?: string, limit?: number, offset?: number, ucans?: string[]): Promise<WorkspaceListResult>;
629
698
  slice(path: string, offset?: number, limit?: number, ucans?: string[]): Promise<WorkspaceSliceResult>;
@@ -658,6 +727,35 @@ declare class WorkspaceManager {
658
727
  append(path: string, value: unknown, ucans?: string[]): Promise<WorkspaceAppendResult>;
659
728
  copy(from: string, to: string, ucans?: string[]): Promise<WorkspaceCopyResult>;
660
729
  }
730
+ /**
731
+ * A {@link WorkspaceManager} bound to one execution scope, so `t/`, `n/` and
732
+ * `c/` scratch resolve without the caller hand-building the physical paths
733
+ * (`g/<agent>/sessions/<sid>/c/...` and friends). Reads only — scoped scratch
734
+ * is written from inside the execution that owns it, where the shorthands
735
+ * already resolve from the request's own context.
736
+ *
737
+ * Obtained from {@link WorkspaceManager.scoped}; every method mirrors the
738
+ * job-free read of the same name.
739
+ */
740
+ declare class ScopedWorkspace {
741
+ private workspace;
742
+ /** The scope every read on this handle resolves against. */
743
+ readonly scope: ExecutionScope;
744
+ constructor(workspace: WorkspaceManager,
745
+ /** The scope every read on this handle resolves against. */
746
+ scope: ExecutionScope);
747
+ read(path: string, maxSize?: number): Promise<WorkspaceReadResult>;
748
+ list(path: string, limit?: number, offset?: number): Promise<WorkspaceListResult>;
749
+ slice(path: string, offset?: number, limit?: number): Promise<WorkspaceSliceResult>;
750
+ inspect(path: string, budget?: number, compact?: boolean): Promise<WorkspaceInspectResult>;
751
+ count(path: string, opts?: {
752
+ depth?: number;
753
+ }): Promise<WorkspaceCountResult>;
754
+ aggregate(path: string, opts?: {
755
+ depth?: number;
756
+ groupBy?: string;
757
+ }): Promise<WorkspaceAggregateResult>;
758
+ }
661
759
 
662
760
  interface UCANManagerVenue {
663
761
  operations: OperationRunner;
@@ -1149,6 +1247,23 @@ interface OperationPayload {
1149
1247
  interface ContentHashResult {
1150
1248
  hash: string;
1151
1249
  }
1250
+ /** Paging/ordering for {@link JobManager.history}. */
1251
+ interface JobHistoryOptions {
1252
+ /** Rows to skip from the ordered start (default 0). */
1253
+ offset?: number;
1254
+ /** Maximum rows to return (default 50). */
1255
+ limit?: number;
1256
+ /** `desc` (default) is newest-first, the order a history view wants. */
1257
+ order?: 'asc' | 'desc';
1258
+ }
1259
+ /** One page of job history: the rows plus the window they were drawn from. */
1260
+ interface JobHistoryPage {
1261
+ items: JobMetadata[];
1262
+ /** Authoritative total at the moment the page was read. */
1263
+ total: number;
1264
+ offset: number;
1265
+ limit: number;
1266
+ }
1152
1267
  interface JobMetadata {
1153
1268
  id?: string;
1154
1269
  name?: string;
@@ -1438,17 +1553,78 @@ interface AgentSessionMetadata {
1438
1553
  turnCount?: number;
1439
1554
  [key: string]: unknown;
1440
1555
  }
1556
+ /** The four roles a stored conversation turn may carry. */
1557
+ type AgentSessionRole = 'system' | 'user' | 'assistant' | 'tool';
1558
+ /**
1559
+ * One persisted conversation turn (AGENT_CONTEXT.md §1.1 `Turn`).
1560
+ *
1561
+ * Most turns carry text in `content`; a tool result may instead carry its typed
1562
+ * map or vector in `structuredContent`. Provider-specific continuation state
1563
+ * (`providerState`) is deliberately absent — it is opaque, is omitted from
1564
+ * retrospective session projections, and is not part of a readable transcript.
1565
+ */
1566
+ interface AgentSessionMessage {
1567
+ role: AgentSessionRole;
1568
+ content?: unknown;
1569
+ /** A typed tool result, in place of textual `content`. */
1570
+ structuredContent?: unknown;
1571
+ /** Present on an assistant turn that called tools. */
1572
+ toolCalls?: unknown[];
1573
+ /** A `tool` turn's matching call id. */
1574
+ id?: string;
1575
+ name?: string;
1576
+ isError?: boolean;
1577
+ ts?: number;
1578
+ source?: string;
1579
+ caller?: string;
1580
+ /** The job that delivered this turn — links it back to the calling job. */
1581
+ jobId?: string;
1582
+ tokens?: number;
1583
+ }
1584
+ /**
1585
+ * An archived run of turns that compaction replaced with a summary
1586
+ * (AGENT_CONTEXT.md §1.1 `CompactedSegment`). `items` is the exact recursively
1587
+ * archived vector, so no history is lost — {@link AgentSession.conversation}
1588
+ * expands it back into the transcript.
1589
+ */
1590
+ interface AgentCompactedSegment {
1591
+ summary: string;
1592
+ /** How many turns the summary stands in for. */
1593
+ turns?: number;
1594
+ items: unknown[];
1595
+ }
1441
1596
  /** A session record read directly from `g/<agentId>/sessions`. */
1442
1597
  interface AgentSession {
1443
1598
  id: string;
1444
1599
  metadata: AgentSessionMetadata;
1445
1600
  pending: unknown[];
1601
+ /**
1602
+ * Raw goal-tree frames, newest last. `frames[0]` is the root frame and a flat
1603
+ * `llmagent` session has only that one. Kept verbatim for callers that need
1604
+ * frame-level state; for the readable transcript use {@link conversation}.
1605
+ */
1446
1606
  frames: unknown[];
1607
+ /**
1608
+ * The session transcript: every frame's turns, in order, with compacted
1609
+ * segments expanded back into the turns they archived.
1610
+ *
1611
+ * This is the field a chat UI renders. Assembling it from `frames` means
1612
+ * knowing that turns live at `frames[].conversation`, that an entry there may
1613
+ * be an archived segment rather than a turn, and that segments nest — layout
1614
+ * knowledge that belongs here rather than in every consumer.
1615
+ */
1616
+ conversation: AgentSessionMessage[];
1447
1617
  wakeTime?: number;
1448
1618
  }
1449
1619
  interface AgentSessionListOptions {
1450
1620
  offset?: number;
1451
1621
  limit?: number;
1622
+ /**
1623
+ * `desc` is newest-first. Session ids are timestamp-prefixed, so the index is
1624
+ * already chronological and the window is taken from its end — the order
1625
+ * holds across pages, unlike sorting one page after the fact.
1626
+ */
1627
+ order?: 'asc' | 'desc';
1452
1628
  }
1453
1629
  interface AgentSessionPage {
1454
1630
  items: AgentSession[];
@@ -1640,6 +1816,33 @@ interface WorkspaceAppendResult {
1640
1816
  /** 0.3.0: true iff a new parent path was built. */
1641
1817
  pathCreated?: boolean;
1642
1818
  }
1819
+ /**
1820
+ * The execution context a `t/`, `n/` or `c/` scratch read resolves against.
1821
+ *
1822
+ * A GET carries no execution context of its own, so the venue cannot know which
1823
+ * job, agent or session a bare shorthand means. These selectors supply it
1824
+ * explicitly (covia#230) and are the only way to read scoped scratch job-free.
1825
+ *
1826
+ * Each namespace consumes a different part of the scope, and the venue rejects
1827
+ * a selector its namespace does not use:
1828
+ *
1829
+ * | Path | Needs | Resolves to |
1830
+ * |------|------------------|----------------------------------------------|
1831
+ * | `n/` | `agent` | `g/<agent>/n/...` |
1832
+ * | `c/` | `agent`+`session`| `g/<agent>/sessions/<session>/c/...` |
1833
+ * | `t/` | `agent`+`task` | `j/<task>/temp/...` |
1834
+ *
1835
+ * A bound scope may therefore carry all three; each read sends only what its
1836
+ * own namespace needs.
1837
+ */
1838
+ interface ExecutionScope {
1839
+ /** Agent id (`alice`) or a full agent DID. Required for every scoped read. */
1840
+ agent: string;
1841
+ /** Task id — the `agent:request` Job whose record holds `t/` scratch. */
1842
+ task?: string;
1843
+ /** Session id, for `c/` session scratch. */
1844
+ session?: string;
1845
+ }
1643
1846
  interface WorkspaceListInput {
1644
1847
  path?: string;
1645
1848
  limit?: number;
@@ -2282,4 +2485,4 @@ declare function grant(ownerPrivateKey: Uint8Array, audienceDID: string, withRes
2282
2485
  */
2283
2486
  declare function relayDelegation(privateKey: Uint8Array, venueDID: string, lifetimeSeconds: number, caps?: UCANCapability[]): string;
2284
2487
 
2285
- export { type A2AAuthRef, type A2AImportAgentInput, type A2AImportAgentResult, A2AManager, type A2AMessage, type A2APart, type A2ATask, type AdapterInfo, AdapterManager, type AdaptersResult, Agent, type AgentCancelTaskInput, type AgentCard, type AgentChatInput, type AgentChatResult, type AgentCompleteTaskResult, type AgentContextInput, type AgentCreateInput, type AgentCreateResult, type AgentCycleEndEvent, type AgentCycleStartEvent, type AgentDeleteInput, type AgentDeleteResult, type AgentEvent, type AgentEventEnvelope, type AgentFailTaskResult, type AgentForkInput, type AgentForkResult, type AgentInferenceEndEvent, type AgentInferenceStartEvent, type AgentInfoResult, type AgentListInput, type AgentListResult, AgentManager, type AgentMessageInput, type AgentMessageResult, type AgentRenameSessionResult, type AgentRequestInput, type AgentRequestResult, type AgentResumeInput, type AgentRunEndEvent, type AgentRunStartEvent, type AgentSession, type AgentSessionListOptions, type AgentSessionMetadata, AgentSessionNotFoundError, type AgentSessionPage, AgentStatus, type AgentStatusEvent, type AgentSuspendResult, type AgentToolResultEvent, type AgentToolStartEvent, type AgentTriggerInput, type AgentTriggerResult, type AgentUpdateInput, Asset, type AssetID, type AssetList, type AssetListItem, type AssetListOptions, AssetManager, type AssetMetadata, type AssetMetadataStore, AssetNotFoundError, type AssetPinResult, type AssetSummary, Auth, type AuthenticationKeyEntry, type AuthenticationKeysMap, type AuthenticationKeysResult, type AuthenticatorRevokeResult, BasicAuth, BearerAuth, ChatSession, type ConnectionAttempt, type ContentDetails, type ContentHashResult, CoviaConnectionError, type CoviaConnectionErrorOptions, CoviaError, CoviaTimeoutError, type Credentials, CredentialsHTTP, type DIDDocument, type DIDURL, type DLFSDrivesResult, type DLFSEntry, type DLFSListResult, DLFSManager, DataAsset, Ed25519Auth, type ExpandedAssetList, type FunctionInfo, type FunctionsResult, Grid, GridError, type GroupCount, type InvokeOptions, type InvokePayload, Job, JobFailedError, JobManager, type JobMetadata, JobNotFoundError, JobStatus, KeyPairAuth, type MCPDiscovery, type MemoryChange, type MemoryEntry, MemoryManager, type MyAssetList, Namespace, NoAuth, NotFoundError, Operation, type OperationDetails, type OperationInfo, OperationManager, type OperationPayload, type OperationRunner, RateLimitError, RunStatus, type SSEEvent, type SecretExtractInput, type SecretExtractResult, SecretManager, type SecretSetInput, type SecretSetResult, type Skill, type SkillFacet, SkillManager, type SkillMetadata, type StatsData, type StatusData, type UCANAttenuation, type UCANCapability, type UCANIssueInput, type UCANIssueResult, UCANManager, type UCANVerifyResult, UnsupportedVenueFeatureError, type UserInfo, type UserListResult, UserManager, type UserSummary, VENUE_RELAY, Venue, type VenueConstructor, type VenueData, VenueIdentityChangedError, type VenueInterface, type VenueOptions, type WorkspaceAggregateResult, type WorkspaceAppendInput, type WorkspaceAppendResult, type WorkspaceCopyResult, type WorkspaceCountResult, type WorkspaceDeleteInput, type WorkspaceDeleteResult, type WorkspaceInspectInput, type WorkspaceInspectResult, type WorkspaceListInput, type WorkspaceListResult, WorkspaceManager, type WorkspaceReadInput, type WorkspaceReadResult, type WorkspaceSliceInput, type WorkspaceSliceResult, type WorkspaceWriteInput, type WorkspaceWriteResult, assetHash, createSSEEvent, createUCANJWT, decodePublicKey, didFor, didFromPublicKey, didMethod, didUrl, encodePublicKey, fetchStreamWithError, fetchWithError, generateKeyPair, getAssetIdFromPath, getAssetIdFromVenueId, getAssetMetadataStore, getParsedAssetId, grant, hexToPrivateKey, identityToken, isDid, isJobComplete, isJobFinished, isJobPaused, logger, parseDidUrl, parseRetryAfterMs, parseSSEStream, privateKeyToHex, relayDelegation, retryDelayMs, setAssetMetadataStore, venueBaseUrlCandidates };
2488
+ export { type A2AAuthRef, type A2AImportAgentInput, type A2AImportAgentResult, A2AManager, type A2AMessage, type A2APart, type A2ATask, type AdapterInfo, AdapterManager, type AdaptersResult, Agent, type AgentCancelTaskInput, type AgentCard, type AgentChatInput, type AgentChatResult, type AgentCompactedSegment, type AgentCompleteTaskResult, type AgentContextInput, type AgentCreateInput, type AgentCreateResult, type AgentCycleEndEvent, type AgentCycleStartEvent, type AgentDeleteInput, type AgentDeleteResult, type AgentEvent, type AgentEventEnvelope, type AgentFailTaskResult, type AgentForkInput, type AgentForkResult, type AgentInferenceEndEvent, type AgentInferenceStartEvent, type AgentInfoResult, type AgentListInput, type AgentListResult, AgentManager, type AgentMessageInput, type AgentMessageResult, type AgentRenameSessionResult, type AgentRequestInput, type AgentRequestResult, type AgentResumeInput, type AgentRunEndEvent, type AgentRunStartEvent, type AgentSession, type AgentSessionListOptions, type AgentSessionMessage, type AgentSessionMetadata, AgentSessionNotFoundError, type AgentSessionPage, type AgentSessionRole, AgentStatus, type AgentStatusEvent, type AgentSuspendResult, type AgentToolResultEvent, type AgentToolStartEvent, type AgentTriggerInput, type AgentTriggerResult, type AgentUpdateInput, Asset, type AssetID, type AssetList, type AssetListItem, type AssetListOptions, AssetManager, type AssetMetadata, type AssetMetadataStore, AssetNotFoundError, type AssetPinResult, type AssetSummary, Auth, type AuthenticationKeyEntry, type AuthenticationKeysMap, type AuthenticationKeysResult, type AuthenticatorRevokeResult, BasicAuth, BearerAuth, ChatSession, type ConnectionAttempt, type ContentDetails, type ContentHashResult, CoviaConnectionError, type CoviaConnectionErrorOptions, CoviaError, CoviaTimeoutError, type Credentials, CredentialsHTTP, type DIDDocument, type DIDURL, type DLFSDrivesResult, type DLFSEntry, type DLFSListResult, DLFSManager, DataAsset, Ed25519Auth, type ExecutionScope, type ExpandedAssetList, type FunctionInfo, type FunctionsResult, Grid, GridError, type GroupCount, type InvokeOptions, type InvokePayload, Job, JobFailedError, type JobHistoryOptions, type JobHistoryPage, JobManager, type JobMetadata, JobNotFoundError, JobStatus, KeyPairAuth, type MCPDiscovery, type MemoryChange, type MemoryEntry, MemoryManager, type MyAssetList, Namespace, NoAuth, NotFoundError, Operation, type OperationDetails, type OperationInfo, OperationManager, type OperationPayload, type OperationRunner, RateLimitError, RunStatus, type SSEEvent, ScopedWorkspace, type SecretExtractInput, type SecretExtractResult, SecretManager, type SecretSetInput, type SecretSetResult, type Skill, type SkillFacet, SkillManager, type SkillMetadata, type StatsData, type StatusData, type UCANAttenuation, type UCANCapability, type UCANIssueInput, type UCANIssueResult, UCANManager, type UCANVerifyResult, UnsupportedVenueFeatureError, type UserInfo, type UserListResult, UserManager, type UserSummary, VENUE_RELAY, Venue, type VenueConstructor, type VenueData, VenueIdentityChangedError, type VenueInterface, type VenueOptions, type WorkspaceAggregateResult, type WorkspaceAppendInput, type WorkspaceAppendResult, type WorkspaceCopyResult, type WorkspaceCountResult, type WorkspaceDeleteInput, type WorkspaceDeleteResult, type WorkspaceInspectInput, type WorkspaceInspectResult, type WorkspaceListInput, type WorkspaceListResult, WorkspaceManager, type WorkspaceReadInput, type WorkspaceReadResult, type WorkspaceSliceInput, type WorkspaceSliceResult, type WorkspaceWriteInput, type WorkspaceWriteResult, assetHash, createSSEEvent, createUCANJWT, decodePublicKey, didFor, didFromPublicKey, didMethod, didUrl, encodePublicKey, fetchStreamWithError, fetchWithError, generateKeyPair, getAssetIdFromPath, getAssetIdFromVenueId, getAssetMetadataStore, getParsedAssetId, grant, hexToPrivateKey, identityToken, isDid, isJobComplete, isJobFinished, isJobPaused, logger, parseDidUrl, parseRetryAfterMs, parseSSEStream, privateKeyToHex, relayDelegation, retryDelayMs, setAssetMetadataStore, venueBaseUrlCandidates };
package/dist/index.js CHANGED
@@ -1743,16 +1743,46 @@ async function sliceAll(workspace, path, pageSize = SLICE_PAGE_SIZE) {
1743
1743
  }
1744
1744
  return items;
1745
1745
  }
1746
+ function descWindow(total, offset, limit) {
1747
+ const end = Math.max(0, total - offset);
1748
+ return [Math.max(0, end - limit), end];
1749
+ }
1746
1750
 
1747
1751
  // src/AgentManager.ts
1752
+ var SESSION_ROLES = ["system", "user", "assistant", "tool"];
1753
+ var MAX_COMPACTION_DEPTH = 32;
1754
+ function collectTurns(entries, out, depth = 0) {
1755
+ if (depth > MAX_COMPACTION_DEPTH) return;
1756
+ for (const entry of entries) {
1757
+ const e = record(entry);
1758
+ if (!e) continue;
1759
+ if (Array.isArray(e.items) && typeof e.summary === "string") {
1760
+ collectTurns(e.items, out, depth + 1);
1761
+ continue;
1762
+ }
1763
+ if (typeof e.role === "string" && SESSION_ROLES.includes(e.role)) {
1764
+ out.push({ ...e, role: e.role });
1765
+ }
1766
+ }
1767
+ }
1768
+ function sessionConversation(frames) {
1769
+ const turns = [];
1770
+ for (const frame of frames) {
1771
+ const entries = record(frame)?.conversation;
1772
+ if (Array.isArray(entries)) collectTurns(entries, turns);
1773
+ }
1774
+ return turns;
1775
+ }
1748
1776
  function sessionRecord(sessionId, value) {
1749
1777
  const session = record(value) ?? {};
1750
1778
  const meta = record(session.meta);
1779
+ const frames = Array.isArray(session.frames) ? session.frames : [];
1751
1780
  return {
1752
1781
  id: sessionId,
1753
1782
  metadata: meta ?? {},
1754
1783
  pending: Array.isArray(session.pending) ? session.pending : [],
1755
- frames: Array.isArray(session.frames) ? session.frames : [],
1784
+ frames,
1785
+ conversation: sessionConversation(frames),
1756
1786
  wakeTime: typeof session.wakeTime === "number" ? session.wakeTime : void 0
1757
1787
  };
1758
1788
  }
@@ -1905,11 +1935,31 @@ var AgentManager = class {
1905
1935
  * available on each record's `value` while common runtime fields are typed.
1906
1936
  */
1907
1937
  async listSessions(agentId, options = {}) {
1908
- const result = await this.venue.workspace.slice(
1909
- `g/${agentId}/sessions`,
1910
- options.offset,
1911
- options.limit
1912
- );
1938
+ const path = `g/${agentId}/sessions`;
1939
+ if ((options.order ?? "asc") === "asc") {
1940
+ return this._sessionPage(path, await this.venue.workspace.slice(
1941
+ path,
1942
+ options.offset,
1943
+ options.limit
1944
+ ), options.offset ?? 0, options.limit);
1945
+ }
1946
+ const offset = Math.max(0, options.offset ?? 0);
1947
+ const limit = options.limit;
1948
+ const guess = (await this.venue.workspace.count(path)).count ?? 0;
1949
+ if (limit === 0) return { items: [], total: guess, offset, limit: 0 };
1950
+ const read = async (total2) => {
1951
+ const [start, end] = descWindow(total2, offset, limit ?? total2);
1952
+ return end > start ? this.venue.workspace.slice(path, start, end - start) : { exists: true, values: [], count: total2, offset: start };
1953
+ };
1954
+ let result = await read(guess);
1955
+ const total = result.count ?? guess;
1956
+ if (total !== guess) result = await read(total);
1957
+ const page = this._sessionPage(path, result, offset, limit);
1958
+ page.items.reverse();
1959
+ return page;
1960
+ }
1961
+ /** Shape a `{key, value}` session slice into a page. */
1962
+ _sessionPage(_path, result, offset, limit) {
1913
1963
  const sessions = (result.values ?? []).flatMap((entry) => {
1914
1964
  const pair = record(entry);
1915
1965
  if (!pair || typeof pair.key !== "string") return [];
@@ -1918,8 +1968,8 @@ var AgentManager = class {
1918
1968
  return {
1919
1969
  items: sessions,
1920
1970
  total: result.count ?? sessions.length,
1921
- offset: result.offset ?? options.offset ?? 0,
1922
- limit: options.limit ?? sessions.length
1971
+ offset,
1972
+ limit: limit ?? sessions.length
1923
1973
  };
1924
1974
  }
1925
1975
  /** Read one agent session directly from the workspace Values surface. */
@@ -2111,6 +2161,24 @@ var Job = class {
2111
2161
  };
2112
2162
 
2113
2163
  // src/JobManager.ts
2164
+ var JOB_INDEX = "j";
2165
+ var DEFAULT_HISTORY_LIMIT = 50;
2166
+ var MAX_SLICE_LIMIT = 100;
2167
+ function isSliceCapError(error) {
2168
+ return error instanceof Error && error.message.includes("exceeds maxSize");
2169
+ }
2170
+ function jobRecords(values) {
2171
+ const records = [];
2172
+ for (const entry of values) {
2173
+ const pair = record(entry);
2174
+ const value = record(pair?.value);
2175
+ if (!value) continue;
2176
+ const meta = value;
2177
+ const key = typeof pair?.key === "string" ? pair.key : "";
2178
+ records.push({ ...meta, id: meta.id ?? `0x${key}` });
2179
+ }
2180
+ return records;
2181
+ }
2114
2182
  function jobPage(value) {
2115
2183
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
2116
2184
  throw new CoviaError("Venue returned an invalid jobs listing");
@@ -2152,6 +2220,70 @@ var JobManager = class {
2152
2220
  }
2153
2221
  return items;
2154
2222
  }
2223
+ /**
2224
+ * A page of job history: `JobMetadata` rows, newest-first by default, with an
2225
+ * authoritative total.
2226
+ *
2227
+ * `list()` answers "which jobs exist" as ids; a history view needs a bounded
2228
+ * window of *records* and a count to page against. This reads the job index
2229
+ * directly, so each row arrives with its metadata already attached — the
2230
+ * alternative, listing ids and then fetching each one, is an N+1 fan-out per
2231
+ * page. Reads stay job-free (covia#177): paging job history must not itself
2232
+ * write a job record per page.
2233
+ *
2234
+ * The total is read before a `desc` window can be placed, so the index may
2235
+ * grow in between; the page self-corrects once against the count the slice
2236
+ * itself reports, which is authoritative for the rows returned with it.
2237
+ */
2238
+ async history(options = {}) {
2239
+ const offset = Math.max(0, Math.trunc(options.offset ?? 0));
2240
+ const limit = Math.max(0, Math.trunc(options.limit ?? DEFAULT_HISTORY_LIMIT));
2241
+ const descending = (options.order ?? "desc") === "desc";
2242
+ if (limit === 0) {
2243
+ const { count } = await this.venue.workspace.count(JOB_INDEX);
2244
+ return { items: [], total: count ?? 0, offset, limit };
2245
+ }
2246
+ if (!descending) {
2247
+ const asc = await this._readJobRange(offset, offset + limit);
2248
+ return { items: jobRecords(asc.values), total: asc.count ?? offset + asc.values.length, offset, limit };
2249
+ }
2250
+ const guess = (await this.venue.workspace.count(JOB_INDEX)).count ?? 0;
2251
+ let read = await this._readJobRange(...descWindow(guess, offset, limit));
2252
+ const total = read.count ?? guess;
2253
+ if (total !== guess) {
2254
+ read = await this._readJobRange(...descWindow(total, offset, limit));
2255
+ }
2256
+ return { items: jobRecords(read.values).reverse(), total: read.count ?? total, offset, limit };
2257
+ }
2258
+ /**
2259
+ * Read index ranks `[start, end)` in cap-aware chunks, halving on an oversize
2260
+ * response and skipping a record that is individually oversize.
2261
+ */
2262
+ async _readJobRange(start, end) {
2263
+ const values = [];
2264
+ let count;
2265
+ let pos = start;
2266
+ let chunk = MAX_SLICE_LIMIT;
2267
+ while (pos < end) {
2268
+ const want = Math.min(chunk, end - pos);
2269
+ try {
2270
+ const page = await this.venue.workspace.slice(JOB_INDEX, pos, want);
2271
+ count = page.count ?? count;
2272
+ const got = page.values ?? [];
2273
+ values.push(...got);
2274
+ if (got.length < want) break;
2275
+ pos += got.length;
2276
+ } catch (error) {
2277
+ if (!isSliceCapError(error)) throw error;
2278
+ if (want > 1) {
2279
+ chunk = Math.max(1, Math.floor(want / 2));
2280
+ continue;
2281
+ }
2282
+ pos += 1;
2283
+ }
2284
+ }
2285
+ return { count, values };
2286
+ }
2155
2287
  async get(jobId) {
2156
2288
  try {
2157
2289
  const data = await venueJson(this.venue, `/api/v1/jobs/${jobId}`);
@@ -2583,12 +2715,52 @@ var OperationManager = class {
2583
2715
  };
2584
2716
 
2585
2717
  // src/WorkspaceManager.ts
2718
+ var SCOPED_NAMESPACES = ["t", "n", "c"];
2719
+ var SCOPE_UNEXPANDED = /Cannot use '[tnc]\/' prefix outside/;
2720
+ function scopedNamespaceOf(path) {
2721
+ const head = path.split("/", 1)[0];
2722
+ return SCOPED_NAMESPACES.includes(head) ? head : void 0;
2723
+ }
2724
+ function scopeParamsFor(ns, scope) {
2725
+ const { agent, task, session } = scope;
2726
+ switch (ns) {
2727
+ case "n":
2728
+ return { agent };
2729
+ case "c":
2730
+ if (!session) throw new CoviaError("A 'c/' scratch read needs a session in its scope");
2731
+ return { agent, session };
2732
+ case "t":
2733
+ if (!task) throw new CoviaError("A 't/' scratch read needs a task in its scope");
2734
+ return { agent, task };
2735
+ }
2736
+ }
2737
+ function expandScopedPath(path, ns, scope) {
2738
+ if (scope.agent.startsWith("did:")) {
2739
+ throw new CoviaError(
2740
+ "This venue cannot expand scoped scratch paths and the SDK cannot do it for a DID-qualified agent \u2014 pass a bare agent id, or upgrade the venue (covia#230)"
2741
+ );
2742
+ }
2743
+ const suffix = path.slice(ns.length);
2744
+ switch (ns) {
2745
+ case "n":
2746
+ return `g/${scope.agent}/n${suffix}`;
2747
+ case "c":
2748
+ return `g/${scope.agent}/sessions/${scope.session}/c${suffix}`;
2749
+ case "t":
2750
+ return `j/${scope.task}/temp${suffix}`;
2751
+ }
2752
+ }
2586
2753
  var WorkspaceManager = class {
2587
2754
  constructor(venue) {
2588
2755
  this.venue = venue;
2589
2756
  // Whether this venue serves GET /api/v1/values/* — flipped on the first 404
2590
2757
  // so pre-0.3 venues pay the probe once, not one failed GET per read.
2591
2758
  this.valuesSupported = true;
2759
+ // Whether this venue expands t/, n/ and c/ from explicit agent/task/session
2760
+ // selectors (covia#230) — flipped off the first time a venue answers a
2761
+ // selector-bearing read by complaining the shorthand has no execution scope,
2762
+ // after which scoped reads expand client-side instead.
2763
+ this.scopeExpansionSupported = true;
2592
2764
  }
2593
2765
  /** GET a job-free `/api/v1/values/{op}` read; omit undefined params. */
2594
2766
  _values(op, params) {
@@ -2622,6 +2794,52 @@ var WorkspaceManager = class {
2622
2794
  }
2623
2795
  return this.unsupported(`workspace ${op} reads`);
2624
2796
  }
2797
+ /**
2798
+ * A job-free Values read of an execution-scoped path, preferring the venue's
2799
+ * own selector expansion and falling back to the client-side one.
2800
+ *
2801
+ * The venue is preferred because it expands *before* the capability check, so
2802
+ * authorisation applies to the same canonical resource that is ultimately
2803
+ * read — a caller cannot substitute a sibling or foreign agent's scratch.
2804
+ * The fallback reaches the same resource but re-derives the layout here.
2805
+ */
2806
+ async scopedRead(op, params, scope) {
2807
+ const path = String(params.path ?? "");
2808
+ const ns = scopedNamespaceOf(path);
2809
+ if (!ns) return this.valuesRead(op, params);
2810
+ if (this.scopeExpansionSupported) {
2811
+ try {
2812
+ return await this.valuesRead(op, { ...params, ...scopeParamsFor(ns, scope) });
2813
+ } catch (e) {
2814
+ if (!(e instanceof Error) || !SCOPE_UNEXPANDED.test(e.message)) throw e;
2815
+ this.scopeExpansionSupported = false;
2816
+ }
2817
+ }
2818
+ scopeParamsFor(ns, scope);
2819
+ return this.valuesRead(op, { ...params, path: expandScopedPath(path, ns, scope) });
2820
+ }
2821
+ /**
2822
+ * Bind an execution scope, giving job-free reads of the `t/`, `n/` and `c/`
2823
+ * scratch shorthands that a bare GET cannot otherwise resolve (covia#177
2824
+ * forbids minting a Job for a read; the shorthands need context a GET lacks).
2825
+ *
2826
+ * ```ts
2827
+ * const scratch = venue.workspace.scoped({ agent: 'alice', task: taskId });
2828
+ * await scratch.read('t/snapshot');
2829
+ * await scratch.list('t/');
2830
+ * ```
2831
+ *
2832
+ * Ordinary paths still work through the handle, so a caller rendering a task
2833
+ * inspector can use one reader for both scratch and workspace. Capability
2834
+ * behaviour is unchanged: the caller needs read caps on the agent's subtree.
2835
+ */
2836
+ scoped(scope) {
2837
+ return new ScopedWorkspace(this, scope);
2838
+ }
2839
+ /** @internal — the scoped read used by {@link ScopedWorkspace}. */
2840
+ _scopedRead(op, params, scope) {
2841
+ return this.scopedRead(op, params, scope);
2842
+ }
2625
2843
  // ── job-free reads (#177) ───────────────────────────────────────────────────
2626
2844
  async read(path, maxSize, ucans) {
2627
2845
  if (ucans?.length) return this.unsupported("UCAN-authorised workspace reads");
@@ -2682,6 +2900,30 @@ var WorkspaceManager = class {
2682
2900
  return this.venue.operations.run("v/ops/covia/copy", { from, to }, { ucans });
2683
2901
  }
2684
2902
  };
2903
+ var ScopedWorkspace = class {
2904
+ constructor(workspace, scope) {
2905
+ this.workspace = workspace;
2906
+ this.scope = scope;
2907
+ }
2908
+ read(path, maxSize) {
2909
+ return this.workspace._scopedRead("read", { path, maxSize }, this.scope);
2910
+ }
2911
+ list(path, limit, offset) {
2912
+ return this.workspace._scopedRead("list", { path, limit, offset }, this.scope);
2913
+ }
2914
+ slice(path, offset, limit) {
2915
+ return this.workspace._scopedRead("slice", { path, offset, limit }, this.scope);
2916
+ }
2917
+ inspect(path, budget, compact) {
2918
+ return this.workspace._scopedRead("inspect", { path, budget, compact }, this.scope);
2919
+ }
2920
+ count(path, opts = {}) {
2921
+ return this.workspace._scopedRead("count", { path, depth: opts.depth }, this.scope);
2922
+ }
2923
+ aggregate(path, opts = {}) {
2924
+ return this.workspace._scopedRead("aggregate", { path, depth: opts.depth, groupBy: opts.groupBy }, this.scope);
2925
+ }
2926
+ };
2685
2927
 
2686
2928
  // src/UCANManager.ts
2687
2929
  var UCANManager = class {
@@ -3053,6 +3295,15 @@ var Agent = class _Agent {
3053
3295
  async getSession(sessionId) {
3054
3296
  return this._agents.getSession(this.id, sessionId);
3055
3297
  }
3298
+ /**
3299
+ * One session's transcript — its turns in order, with compacted segments
3300
+ * expanded back into the turns they archived.
3301
+ *
3302
+ * Shorthand for `getSession(sessionId)` when only the conversation is wanted.
3303
+ */
3304
+ async transcript(sessionId) {
3305
+ return (await this._agents.getSession(this.id, sessionId)).conversation;
3306
+ }
3056
3307
  /** Stream this agent's run-loop events. See `AgentManager.events`. */
3057
3308
  async *events(options) {
3058
3309
  yield* this._agents.events(this.id, options);
@@ -3587,6 +3838,7 @@ exports.Operation = Operation;
3587
3838
  exports.OperationManager = OperationManager;
3588
3839
  exports.RateLimitError = RateLimitError;
3589
3840
  exports.RunStatus = RunStatus;
3841
+ exports.ScopedWorkspace = ScopedWorkspace;
3590
3842
  exports.SecretManager = SecretManager;
3591
3843
  exports.SkillManager = SkillManager;
3592
3844
  exports.UCANManager = UCANManager;
package/dist/index.mjs CHANGED
@@ -1741,16 +1741,46 @@ async function sliceAll(workspace, path, pageSize = SLICE_PAGE_SIZE) {
1741
1741
  }
1742
1742
  return items;
1743
1743
  }
1744
+ function descWindow(total, offset, limit) {
1745
+ const end = Math.max(0, total - offset);
1746
+ return [Math.max(0, end - limit), end];
1747
+ }
1744
1748
 
1745
1749
  // src/AgentManager.ts
1750
+ var SESSION_ROLES = ["system", "user", "assistant", "tool"];
1751
+ var MAX_COMPACTION_DEPTH = 32;
1752
+ function collectTurns(entries, out, depth = 0) {
1753
+ if (depth > MAX_COMPACTION_DEPTH) return;
1754
+ for (const entry of entries) {
1755
+ const e = record(entry);
1756
+ if (!e) continue;
1757
+ if (Array.isArray(e.items) && typeof e.summary === "string") {
1758
+ collectTurns(e.items, out, depth + 1);
1759
+ continue;
1760
+ }
1761
+ if (typeof e.role === "string" && SESSION_ROLES.includes(e.role)) {
1762
+ out.push({ ...e, role: e.role });
1763
+ }
1764
+ }
1765
+ }
1766
+ function sessionConversation(frames) {
1767
+ const turns = [];
1768
+ for (const frame of frames) {
1769
+ const entries = record(frame)?.conversation;
1770
+ if (Array.isArray(entries)) collectTurns(entries, turns);
1771
+ }
1772
+ return turns;
1773
+ }
1746
1774
  function sessionRecord(sessionId, value) {
1747
1775
  const session = record(value) ?? {};
1748
1776
  const meta = record(session.meta);
1777
+ const frames = Array.isArray(session.frames) ? session.frames : [];
1749
1778
  return {
1750
1779
  id: sessionId,
1751
1780
  metadata: meta ?? {},
1752
1781
  pending: Array.isArray(session.pending) ? session.pending : [],
1753
- frames: Array.isArray(session.frames) ? session.frames : [],
1782
+ frames,
1783
+ conversation: sessionConversation(frames),
1754
1784
  wakeTime: typeof session.wakeTime === "number" ? session.wakeTime : void 0
1755
1785
  };
1756
1786
  }
@@ -1903,11 +1933,31 @@ var AgentManager = class {
1903
1933
  * available on each record's `value` while common runtime fields are typed.
1904
1934
  */
1905
1935
  async listSessions(agentId, options = {}) {
1906
- const result = await this.venue.workspace.slice(
1907
- `g/${agentId}/sessions`,
1908
- options.offset,
1909
- options.limit
1910
- );
1936
+ const path = `g/${agentId}/sessions`;
1937
+ if ((options.order ?? "asc") === "asc") {
1938
+ return this._sessionPage(path, await this.venue.workspace.slice(
1939
+ path,
1940
+ options.offset,
1941
+ options.limit
1942
+ ), options.offset ?? 0, options.limit);
1943
+ }
1944
+ const offset = Math.max(0, options.offset ?? 0);
1945
+ const limit = options.limit;
1946
+ const guess = (await this.venue.workspace.count(path)).count ?? 0;
1947
+ if (limit === 0) return { items: [], total: guess, offset, limit: 0 };
1948
+ const read = async (total2) => {
1949
+ const [start, end] = descWindow(total2, offset, limit ?? total2);
1950
+ return end > start ? this.venue.workspace.slice(path, start, end - start) : { exists: true, values: [], count: total2, offset: start };
1951
+ };
1952
+ let result = await read(guess);
1953
+ const total = result.count ?? guess;
1954
+ if (total !== guess) result = await read(total);
1955
+ const page = this._sessionPage(path, result, offset, limit);
1956
+ page.items.reverse();
1957
+ return page;
1958
+ }
1959
+ /** Shape a `{key, value}` session slice into a page. */
1960
+ _sessionPage(_path, result, offset, limit) {
1911
1961
  const sessions = (result.values ?? []).flatMap((entry) => {
1912
1962
  const pair = record(entry);
1913
1963
  if (!pair || typeof pair.key !== "string") return [];
@@ -1916,8 +1966,8 @@ var AgentManager = class {
1916
1966
  return {
1917
1967
  items: sessions,
1918
1968
  total: result.count ?? sessions.length,
1919
- offset: result.offset ?? options.offset ?? 0,
1920
- limit: options.limit ?? sessions.length
1969
+ offset,
1970
+ limit: limit ?? sessions.length
1921
1971
  };
1922
1972
  }
1923
1973
  /** Read one agent session directly from the workspace Values surface. */
@@ -2109,6 +2159,24 @@ var Job = class {
2109
2159
  };
2110
2160
 
2111
2161
  // src/JobManager.ts
2162
+ var JOB_INDEX = "j";
2163
+ var DEFAULT_HISTORY_LIMIT = 50;
2164
+ var MAX_SLICE_LIMIT = 100;
2165
+ function isSliceCapError(error) {
2166
+ return error instanceof Error && error.message.includes("exceeds maxSize");
2167
+ }
2168
+ function jobRecords(values) {
2169
+ const records = [];
2170
+ for (const entry of values) {
2171
+ const pair = record(entry);
2172
+ const value = record(pair?.value);
2173
+ if (!value) continue;
2174
+ const meta = value;
2175
+ const key = typeof pair?.key === "string" ? pair.key : "";
2176
+ records.push({ ...meta, id: meta.id ?? `0x${key}` });
2177
+ }
2178
+ return records;
2179
+ }
2112
2180
  function jobPage(value) {
2113
2181
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
2114
2182
  throw new CoviaError("Venue returned an invalid jobs listing");
@@ -2150,6 +2218,70 @@ var JobManager = class {
2150
2218
  }
2151
2219
  return items;
2152
2220
  }
2221
+ /**
2222
+ * A page of job history: `JobMetadata` rows, newest-first by default, with an
2223
+ * authoritative total.
2224
+ *
2225
+ * `list()` answers "which jobs exist" as ids; a history view needs a bounded
2226
+ * window of *records* and a count to page against. This reads the job index
2227
+ * directly, so each row arrives with its metadata already attached — the
2228
+ * alternative, listing ids and then fetching each one, is an N+1 fan-out per
2229
+ * page. Reads stay job-free (covia#177): paging job history must not itself
2230
+ * write a job record per page.
2231
+ *
2232
+ * The total is read before a `desc` window can be placed, so the index may
2233
+ * grow in between; the page self-corrects once against the count the slice
2234
+ * itself reports, which is authoritative for the rows returned with it.
2235
+ */
2236
+ async history(options = {}) {
2237
+ const offset = Math.max(0, Math.trunc(options.offset ?? 0));
2238
+ const limit = Math.max(0, Math.trunc(options.limit ?? DEFAULT_HISTORY_LIMIT));
2239
+ const descending = (options.order ?? "desc") === "desc";
2240
+ if (limit === 0) {
2241
+ const { count } = await this.venue.workspace.count(JOB_INDEX);
2242
+ return { items: [], total: count ?? 0, offset, limit };
2243
+ }
2244
+ if (!descending) {
2245
+ const asc = await this._readJobRange(offset, offset + limit);
2246
+ return { items: jobRecords(asc.values), total: asc.count ?? offset + asc.values.length, offset, limit };
2247
+ }
2248
+ const guess = (await this.venue.workspace.count(JOB_INDEX)).count ?? 0;
2249
+ let read = await this._readJobRange(...descWindow(guess, offset, limit));
2250
+ const total = read.count ?? guess;
2251
+ if (total !== guess) {
2252
+ read = await this._readJobRange(...descWindow(total, offset, limit));
2253
+ }
2254
+ return { items: jobRecords(read.values).reverse(), total: read.count ?? total, offset, limit };
2255
+ }
2256
+ /**
2257
+ * Read index ranks `[start, end)` in cap-aware chunks, halving on an oversize
2258
+ * response and skipping a record that is individually oversize.
2259
+ */
2260
+ async _readJobRange(start, end) {
2261
+ const values = [];
2262
+ let count;
2263
+ let pos = start;
2264
+ let chunk = MAX_SLICE_LIMIT;
2265
+ while (pos < end) {
2266
+ const want = Math.min(chunk, end - pos);
2267
+ try {
2268
+ const page = await this.venue.workspace.slice(JOB_INDEX, pos, want);
2269
+ count = page.count ?? count;
2270
+ const got = page.values ?? [];
2271
+ values.push(...got);
2272
+ if (got.length < want) break;
2273
+ pos += got.length;
2274
+ } catch (error) {
2275
+ if (!isSliceCapError(error)) throw error;
2276
+ if (want > 1) {
2277
+ chunk = Math.max(1, Math.floor(want / 2));
2278
+ continue;
2279
+ }
2280
+ pos += 1;
2281
+ }
2282
+ }
2283
+ return { count, values };
2284
+ }
2153
2285
  async get(jobId) {
2154
2286
  try {
2155
2287
  const data = await venueJson(this.venue, `/api/v1/jobs/${jobId}`);
@@ -2581,12 +2713,52 @@ var OperationManager = class {
2581
2713
  };
2582
2714
 
2583
2715
  // src/WorkspaceManager.ts
2716
+ var SCOPED_NAMESPACES = ["t", "n", "c"];
2717
+ var SCOPE_UNEXPANDED = /Cannot use '[tnc]\/' prefix outside/;
2718
+ function scopedNamespaceOf(path) {
2719
+ const head = path.split("/", 1)[0];
2720
+ return SCOPED_NAMESPACES.includes(head) ? head : void 0;
2721
+ }
2722
+ function scopeParamsFor(ns, scope) {
2723
+ const { agent, task, session } = scope;
2724
+ switch (ns) {
2725
+ case "n":
2726
+ return { agent };
2727
+ case "c":
2728
+ if (!session) throw new CoviaError("A 'c/' scratch read needs a session in its scope");
2729
+ return { agent, session };
2730
+ case "t":
2731
+ if (!task) throw new CoviaError("A 't/' scratch read needs a task in its scope");
2732
+ return { agent, task };
2733
+ }
2734
+ }
2735
+ function expandScopedPath(path, ns, scope) {
2736
+ if (scope.agent.startsWith("did:")) {
2737
+ throw new CoviaError(
2738
+ "This venue cannot expand scoped scratch paths and the SDK cannot do it for a DID-qualified agent \u2014 pass a bare agent id, or upgrade the venue (covia#230)"
2739
+ );
2740
+ }
2741
+ const suffix = path.slice(ns.length);
2742
+ switch (ns) {
2743
+ case "n":
2744
+ return `g/${scope.agent}/n${suffix}`;
2745
+ case "c":
2746
+ return `g/${scope.agent}/sessions/${scope.session}/c${suffix}`;
2747
+ case "t":
2748
+ return `j/${scope.task}/temp${suffix}`;
2749
+ }
2750
+ }
2584
2751
  var WorkspaceManager = class {
2585
2752
  constructor(venue) {
2586
2753
  this.venue = venue;
2587
2754
  // Whether this venue serves GET /api/v1/values/* — flipped on the first 404
2588
2755
  // so pre-0.3 venues pay the probe once, not one failed GET per read.
2589
2756
  this.valuesSupported = true;
2757
+ // Whether this venue expands t/, n/ and c/ from explicit agent/task/session
2758
+ // selectors (covia#230) — flipped off the first time a venue answers a
2759
+ // selector-bearing read by complaining the shorthand has no execution scope,
2760
+ // after which scoped reads expand client-side instead.
2761
+ this.scopeExpansionSupported = true;
2590
2762
  }
2591
2763
  /** GET a job-free `/api/v1/values/{op}` read; omit undefined params. */
2592
2764
  _values(op, params) {
@@ -2620,6 +2792,52 @@ var WorkspaceManager = class {
2620
2792
  }
2621
2793
  return this.unsupported(`workspace ${op} reads`);
2622
2794
  }
2795
+ /**
2796
+ * A job-free Values read of an execution-scoped path, preferring the venue's
2797
+ * own selector expansion and falling back to the client-side one.
2798
+ *
2799
+ * The venue is preferred because it expands *before* the capability check, so
2800
+ * authorisation applies to the same canonical resource that is ultimately
2801
+ * read — a caller cannot substitute a sibling or foreign agent's scratch.
2802
+ * The fallback reaches the same resource but re-derives the layout here.
2803
+ */
2804
+ async scopedRead(op, params, scope) {
2805
+ const path = String(params.path ?? "");
2806
+ const ns = scopedNamespaceOf(path);
2807
+ if (!ns) return this.valuesRead(op, params);
2808
+ if (this.scopeExpansionSupported) {
2809
+ try {
2810
+ return await this.valuesRead(op, { ...params, ...scopeParamsFor(ns, scope) });
2811
+ } catch (e) {
2812
+ if (!(e instanceof Error) || !SCOPE_UNEXPANDED.test(e.message)) throw e;
2813
+ this.scopeExpansionSupported = false;
2814
+ }
2815
+ }
2816
+ scopeParamsFor(ns, scope);
2817
+ return this.valuesRead(op, { ...params, path: expandScopedPath(path, ns, scope) });
2818
+ }
2819
+ /**
2820
+ * Bind an execution scope, giving job-free reads of the `t/`, `n/` and `c/`
2821
+ * scratch shorthands that a bare GET cannot otherwise resolve (covia#177
2822
+ * forbids minting a Job for a read; the shorthands need context a GET lacks).
2823
+ *
2824
+ * ```ts
2825
+ * const scratch = venue.workspace.scoped({ agent: 'alice', task: taskId });
2826
+ * await scratch.read('t/snapshot');
2827
+ * await scratch.list('t/');
2828
+ * ```
2829
+ *
2830
+ * Ordinary paths still work through the handle, so a caller rendering a task
2831
+ * inspector can use one reader for both scratch and workspace. Capability
2832
+ * behaviour is unchanged: the caller needs read caps on the agent's subtree.
2833
+ */
2834
+ scoped(scope) {
2835
+ return new ScopedWorkspace(this, scope);
2836
+ }
2837
+ /** @internal — the scoped read used by {@link ScopedWorkspace}. */
2838
+ _scopedRead(op, params, scope) {
2839
+ return this.scopedRead(op, params, scope);
2840
+ }
2623
2841
  // ── job-free reads (#177) ───────────────────────────────────────────────────
2624
2842
  async read(path, maxSize, ucans) {
2625
2843
  if (ucans?.length) return this.unsupported("UCAN-authorised workspace reads");
@@ -2680,6 +2898,30 @@ var WorkspaceManager = class {
2680
2898
  return this.venue.operations.run("v/ops/covia/copy", { from, to }, { ucans });
2681
2899
  }
2682
2900
  };
2901
+ var ScopedWorkspace = class {
2902
+ constructor(workspace, scope) {
2903
+ this.workspace = workspace;
2904
+ this.scope = scope;
2905
+ }
2906
+ read(path, maxSize) {
2907
+ return this.workspace._scopedRead("read", { path, maxSize }, this.scope);
2908
+ }
2909
+ list(path, limit, offset) {
2910
+ return this.workspace._scopedRead("list", { path, limit, offset }, this.scope);
2911
+ }
2912
+ slice(path, offset, limit) {
2913
+ return this.workspace._scopedRead("slice", { path, offset, limit }, this.scope);
2914
+ }
2915
+ inspect(path, budget, compact) {
2916
+ return this.workspace._scopedRead("inspect", { path, budget, compact }, this.scope);
2917
+ }
2918
+ count(path, opts = {}) {
2919
+ return this.workspace._scopedRead("count", { path, depth: opts.depth }, this.scope);
2920
+ }
2921
+ aggregate(path, opts = {}) {
2922
+ return this.workspace._scopedRead("aggregate", { path, depth: opts.depth, groupBy: opts.groupBy }, this.scope);
2923
+ }
2924
+ };
2683
2925
 
2684
2926
  // src/UCANManager.ts
2685
2927
  var UCANManager = class {
@@ -3051,6 +3293,15 @@ var Agent = class _Agent {
3051
3293
  async getSession(sessionId) {
3052
3294
  return this._agents.getSession(this.id, sessionId);
3053
3295
  }
3296
+ /**
3297
+ * One session's transcript — its turns in order, with compacted segments
3298
+ * expanded back into the turns they archived.
3299
+ *
3300
+ * Shorthand for `getSession(sessionId)` when only the conversation is wanted.
3301
+ */
3302
+ async transcript(sessionId) {
3303
+ return (await this._agents.getSession(this.id, sessionId)).conversation;
3304
+ }
3054
3305
  /** Stream this agent's run-loop events. See `AgentManager.events`. */
3055
3306
  async *events(options) {
3056
3307
  yield* this._agents.events(this.id, options);
@@ -3549,4 +3800,4 @@ function relayDelegation(privateKey, venueDID, lifetimeSeconds, caps = []) {
3549
3800
  (*! noble-ed25519 - MIT License (c) 2019 Paul Miller (paulmillr.com) *)
3550
3801
  */
3551
3802
 
3552
- export { A2AManager, AdapterManager, Agent, AgentManager, AgentSessionNotFoundError, AgentStatus, Asset, AssetManager, AssetNotFoundError, Auth, BasicAuth, BearerAuth, ChatSession, CoviaConnectionError, CoviaError, CoviaTimeoutError, CredentialsHTTP, DLFSManager, DataAsset, Ed25519Auth, Grid, GridError, Job, JobFailedError, JobManager, JobNotFoundError, JobStatus, KeyPairAuth, MemoryManager, Namespace, NoAuth, NotFoundError, Operation, OperationManager, RateLimitError, RunStatus, SecretManager, SkillManager, UCANManager, UnsupportedVenueFeatureError, UserManager, VENUE_RELAY, Venue, VenueIdentityChangedError, WorkspaceManager, assetHash, createSSEEvent, createUCANJWT, decodePublicKey, didFor, didFromPublicKey, didMethod, didUrl, encodePublicKey, fetchStreamWithError, fetchWithError, generateKeyPair, getAssetIdFromPath, getAssetIdFromVenueId, getAssetMetadataStore, getParsedAssetId, grant, hexToPrivateKey, identityToken, isDid, isJobComplete, isJobFinished, isJobPaused, logger, parseDidUrl, parseRetryAfterMs, parseSSEStream, privateKeyToHex, relayDelegation, retryDelayMs, setAssetMetadataStore, venueBaseUrlCandidates };
3803
+ export { A2AManager, AdapterManager, Agent, AgentManager, AgentSessionNotFoundError, AgentStatus, Asset, AssetManager, AssetNotFoundError, Auth, BasicAuth, BearerAuth, ChatSession, CoviaConnectionError, CoviaError, CoviaTimeoutError, CredentialsHTTP, DLFSManager, DataAsset, Ed25519Auth, Grid, GridError, Job, JobFailedError, JobManager, JobNotFoundError, JobStatus, KeyPairAuth, MemoryManager, Namespace, NoAuth, NotFoundError, Operation, OperationManager, RateLimitError, RunStatus, ScopedWorkspace, SecretManager, SkillManager, UCANManager, UnsupportedVenueFeatureError, UserManager, VENUE_RELAY, Venue, VenueIdentityChangedError, WorkspaceManager, assetHash, createSSEEvent, createUCANJWT, decodePublicKey, didFor, didFromPublicKey, didMethod, didUrl, encodePublicKey, fetchStreamWithError, fetchWithError, generateKeyPair, getAssetIdFromPath, getAssetIdFromVenueId, getAssetMetadataStore, getParsedAssetId, grant, hexToPrivateKey, identityToken, isDid, isJobComplete, isJobFinished, isJobPaused, logger, parseDidUrl, parseRetryAfterMs, parseSSEStream, privateKeyToHex, relayDelegation, retryDelayMs, setAssetMetadataStore, venueBaseUrlCandidates };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@covia/covia-sdk",
3
- "version": "1.17.0",
3
+ "version": "1.18.0",
4
4
  "description": "Typescript library for covia-ai",
5
5
  "packageManager": "pnpm@11.9.0",
6
6
  "main": "./dist/index.js",