@modelprofile.com/flexharness-agent 6.0.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.
Files changed (43) hide show
  1. package/dist_ts_agent/index.d.ts +17 -0
  2. package/dist_ts_agent/index.js +14 -0
  3. package/dist_ts_agent/plugins.d.ts +16 -0
  4. package/dist_ts_agent/plugins.js +18 -0
  5. package/dist_ts_agent/smartagent.classes.agent.d.ts +2 -0
  6. package/dist_ts_agent/smartagent.classes.agent.js +95 -0
  7. package/dist_ts_agent/smartagent.classes.session.d.ts +90 -0
  8. package/dist_ts_agent/smartagent.classes.session.js +2139 -0
  9. package/dist_ts_agent/smartagent.classes.toolregistry.d.ts +12 -0
  10. package/dist_ts_agent/smartagent.classes.toolregistry.js +23 -0
  11. package/dist_ts_agent/smartagent.context.d.ts +3 -0
  12. package/dist_ts_agent/smartagent.context.js +247 -0
  13. package/dist_ts_agent/smartagent.events.d.ts +160 -0
  14. package/dist_ts_agent/smartagent.events.js +136 -0
  15. package/dist_ts_agent/smartagent.interfaces.d.ts +275 -0
  16. package/dist_ts_agent/smartagent.interfaces.js +24 -0
  17. package/dist_ts_agent/smartagent.persistence.d.ts +88 -0
  18. package/dist_ts_agent/smartagent.persistence.js +677 -0
  19. package/dist_ts_agent/smartagent.transactions.d.ts +15 -0
  20. package/dist_ts_agent/smartagent.transactions.js +141 -0
  21. package/dist_ts_agent/tool.adapter.d.ts +29 -0
  22. package/dist_ts_agent/tool.adapter.js +60 -0
  23. package/dist_ts_agent/tool.contracts.d.ts +126 -0
  24. package/dist_ts_agent/tool.contracts.js +2 -0
  25. package/dist_ts_agent/tool.persistence.d.ts +23 -0
  26. package/dist_ts_agent/tool.persistence.js +35 -0
  27. package/license.md +21 -0
  28. package/package.json +31 -0
  29. package/readme.md +37 -0
  30. package/ts_agent/index.ts +119 -0
  31. package/ts_agent/plugins.ts +62 -0
  32. package/ts_agent/readme.md +37 -0
  33. package/ts_agent/smartagent.classes.agent.ts +104 -0
  34. package/ts_agent/smartagent.classes.session.ts +2588 -0
  35. package/ts_agent/smartagent.classes.toolregistry.ts +26 -0
  36. package/ts_agent/smartagent.context.ts +280 -0
  37. package/ts_agent/smartagent.events.ts +347 -0
  38. package/ts_agent/smartagent.interfaces.ts +365 -0
  39. package/ts_agent/smartagent.persistence.ts +827 -0
  40. package/ts_agent/smartagent.transactions.ts +193 -0
  41. package/ts_agent/tool.adapter.ts +85 -0
  42. package/ts_agent/tool.contracts.ts +149 -0
  43. package/ts_agent/tool.persistence.ts +64 -0
@@ -0,0 +1,193 @@
1
+ import type {
2
+ IGenerationBegunEvent,
3
+ IGenerationExecutionCompletedEvent,
4
+ IGenerationExecutionStartedEvent,
5
+ IGenerationOutcomeEvent,
6
+ IToolExecutionIntentEvent,
7
+ TAgentControlEvent,
8
+ TAgentEvent,
9
+ TAgentGenerationOutcome,
10
+ } from './smartagent.events.js';
11
+
12
+ export type TAgentGenerationTransactionState =
13
+ | 'begun'
14
+ | 'execution-started'
15
+ | 'execution-completed'
16
+ | TAgentGenerationOutcome
17
+ | 'invalid';
18
+
19
+ export interface IAgentGenerationTransaction {
20
+ generationId: string;
21
+ state: TAgentGenerationTransactionState;
22
+ begun: IGenerationBegunEvent;
23
+ executionStarted?: IGenerationExecutionStartedEvent;
24
+ executionCompleted?: IGenerationExecutionCompletedEvent;
25
+ outcome?: IGenerationOutcomeEvent;
26
+ }
27
+
28
+ const isControlEvent = (event: TAgentEvent): event is TAgentControlEvent =>
29
+ event.type === 'generation-begun'
30
+ || event.type === 'generation-execution-started'
31
+ || event.type === 'generation-execution-completed'
32
+ || event.type === 'generation-outcome'
33
+ || event.type === 'tool-execution-intent'
34
+ || event.type === 'tool-execution-reconciliation';
35
+
36
+ export const getAgentGenerationTransactions = (
37
+ events: readonly TAgentEvent[],
38
+ ): Map<string, IAgentGenerationTransaction> => {
39
+ interface IAccumulatedTransaction {
40
+ begun?: IGenerationBegunEvent;
41
+ begunCount: number;
42
+ begunIndex: number;
43
+ started?: IGenerationExecutionStartedEvent;
44
+ startedCount: number;
45
+ startedIndex: number;
46
+ completed?: IGenerationExecutionCompletedEvent;
47
+ completedCount: number;
48
+ completedIndex: number;
49
+ outcome?: IGenerationOutcomeEvent;
50
+ outcomeCount: number;
51
+ outcomeIndex: number;
52
+ }
53
+ const accumulated = new Map<string, IAccumulatedTransaction>();
54
+ const entryFor = (generationId: string): IAccumulatedTransaction => {
55
+ let entry = accumulated.get(generationId);
56
+ if (!entry) {
57
+ entry = {
58
+ begunCount: 0,
59
+ begunIndex: -1,
60
+ startedCount: 0,
61
+ startedIndex: -1,
62
+ completedCount: 0,
63
+ completedIndex: -1,
64
+ outcomeCount: 0,
65
+ outcomeIndex: -1,
66
+ };
67
+ accumulated.set(generationId, entry);
68
+ }
69
+ return entry;
70
+ };
71
+ for (const [index, event] of events.entries()) {
72
+ if (event.type === 'generation-begun') {
73
+ const entry = entryFor(event.generationId);
74
+ entry.begun ??= event;
75
+ entry.begunCount++;
76
+ if (entry.begunIndex < 0) entry.begunIndex = index;
77
+ } else if (event.type === 'generation-execution-started') {
78
+ const entry = entryFor(event.generationId);
79
+ entry.started ??= event;
80
+ entry.startedCount++;
81
+ if (entry.startedIndex < 0) entry.startedIndex = index;
82
+ } else if (event.type === 'generation-execution-completed') {
83
+ const entry = entryFor(event.generationId);
84
+ entry.completed ??= event;
85
+ entry.completedCount++;
86
+ if (entry.completedIndex < 0) entry.completedIndex = index;
87
+ } else if (event.type === 'generation-outcome') {
88
+ const entry = entryFor(event.generationId);
89
+ entry.outcome ??= event;
90
+ entry.outcomeCount++;
91
+ if (entry.outcomeIndex < 0) entry.outcomeIndex = index;
92
+ }
93
+ }
94
+ const transactions = new Map<string, IAgentGenerationTransaction>();
95
+ for (const [generationId, entry] of accumulated) {
96
+ if (!entry.begun) continue;
97
+ let state: TAgentGenerationTransactionState;
98
+ if (
99
+ entry.begunCount !== 1
100
+ || entry.startedCount > 1
101
+ || entry.completedCount > 1
102
+ || entry.outcomeCount > 1
103
+ || (entry.startedIndex >= 0 && entry.startedIndex < entry.begunIndex)
104
+ || (entry.startedCount === 0 && entry.completedCount > 0)
105
+ || (entry.completedIndex >= 0 && entry.completedIndex < entry.startedIndex)
106
+ || (entry.outcomeIndex >= 0 && entry.outcomeIndex < entry.begunIndex)
107
+ || (entry.outcomeIndex >= 0 && entry.startedIndex > entry.outcomeIndex)
108
+ || (entry.outcomeIndex >= 0 && entry.completedIndex > entry.outcomeIndex)
109
+ || (
110
+ (entry.outcome?.outcome === 'accepted' || entry.outcome?.outcome === 'rejected')
111
+ && entry.completedCount === 0
112
+ )
113
+ ) {
114
+ state = 'invalid';
115
+ } else if (entry.outcome) {
116
+ state = entry.outcome.outcome;
117
+ } else if (entry.completed) {
118
+ state = 'execution-completed';
119
+ } else if (entry.started) {
120
+ state = 'execution-started';
121
+ } else {
122
+ state = 'begun';
123
+ }
124
+ transactions.set(generationId, {
125
+ generationId,
126
+ state,
127
+ begun: entry.begun,
128
+ executionStarted: entry.started,
129
+ executionCompleted: entry.completed,
130
+ outcome: entry.outcome,
131
+ });
132
+ }
133
+ return transactions;
134
+ };
135
+
136
+ export const isTransactionalGeneration = (
137
+ events: readonly TAgentEvent[],
138
+ generationId: string,
139
+ ): boolean => getAgentGenerationTransactions(events).has(generationId);
140
+
141
+ export const filterModelVisibleAgentEvents = (
142
+ events: readonly TAgentEvent[],
143
+ ): TAgentEvent[] => {
144
+ const transactions = getAgentGenerationTransactions(events);
145
+ const visibleEvents: TAgentEvent[] = [];
146
+ for (const event of events) {
147
+ if (isControlEvent(event)) continue;
148
+ if (event.type === 'context-compaction' && event.archivedTransactions) {
149
+ visibleEvents.push({ ...event, archivedTransactions: undefined });
150
+ continue;
151
+ }
152
+ if (!event.generationId) {
153
+ visibleEvents.push(event);
154
+ continue;
155
+ }
156
+ const transaction = transactions.get(event.generationId);
157
+ if (
158
+ !transaction
159
+ || transaction.state === 'accepted'
160
+ || transaction.state === 'execution-started'
161
+ ) visibleEvents.push(event);
162
+ }
163
+ return visibleEvents;
164
+ };
165
+
166
+ export const getOpenAgentGenerationTransactions = (
167
+ events: readonly TAgentEvent[],
168
+ ): IAgentGenerationTransaction[] => [...getAgentGenerationTransactions(events).values()]
169
+ .filter((transaction) =>
170
+ transaction.state !== 'accepted'
171
+ && transaction.state !== 'rejected'
172
+ && transaction.state !== 'interrupted');
173
+
174
+ export const getUncertainToolExecutionIntents = (
175
+ events: readonly TAgentEvent[],
176
+ generationId?: string,
177
+ ): IToolExecutionIntentEvent[] => {
178
+ const uncertain = new Map<string, IToolExecutionIntentEvent>();
179
+ for (const event of events) {
180
+ if (
181
+ event.type === 'tool-execution-intent'
182
+ && (generationId === undefined || event.generationId === generationId)
183
+ ) {
184
+ uncertain.set(event.id, event);
185
+ } else if (event.type === 'tool-result' && event.executionIntentId) {
186
+ const intent = uncertain.get(event.executionIntentId);
187
+ if (intent?.generationId === event.generationId) {
188
+ uncertain.delete(event.executionIntentId);
189
+ }
190
+ }
191
+ }
192
+ return [...uncertain.values()];
193
+ };
@@ -0,0 +1,85 @@
1
+ import type { IToolExecutionContext } from './tool.contracts.js';
2
+
3
+ export interface IToolExecutionAdapterRequest {
4
+ /** Host-defined isolation kind, for example "sandbox" or "container". */
5
+ isolation: string;
6
+ sessionId?: string;
7
+ cwd?: string;
8
+ rootDir?: string;
9
+ abortSignal?: AbortSignal;
10
+ metadata?: Record<string, unknown>;
11
+ }
12
+
13
+ export interface IToolExecutionAdapter {
14
+ readonly id: string;
15
+ create(request: IToolExecutionAdapterRequest): Promise<IToolExecutionContext>;
16
+ destroy?(context: IToolExecutionContext): Promise<void>;
17
+ }
18
+
19
+ export interface IToolExecutionContextLease {
20
+ readonly adapterId: string;
21
+ readonly context: IToolExecutionContext;
22
+ close(): Promise<void>;
23
+ }
24
+
25
+ export class ToolExecutionAdapterCleanupError extends Error {
26
+ constructor(
27
+ public readonly adapter: IToolExecutionAdapter,
28
+ public readonly context: IToolExecutionContext,
29
+ public readonly abortReason: unknown,
30
+ public readonly cleanupError: unknown,
31
+ ) {
32
+ super(`Tool execution adapter "${adapter.id}" failed to clean up after acquisition was aborted.`);
33
+ this.name = 'ToolExecutionAdapterCleanupError';
34
+ }
35
+
36
+ public async retryCleanup(): Promise<void> {
37
+ await this.adapter.destroy?.(this.context);
38
+ }
39
+ }
40
+
41
+ export const acquireToolExecutionContext = async (
42
+ adapter: IToolExecutionAdapter,
43
+ request: IToolExecutionAdapterRequest,
44
+ ): Promise<IToolExecutionContextLease> => {
45
+ if (!adapter.id.trim()) throw new Error('Tool execution adapter id must not be empty.');
46
+ if (!request.isolation.trim()) throw new Error('Tool execution isolation must not be empty.');
47
+ request.abortSignal?.throwIfAborted();
48
+ const context = await adapter.create(request);
49
+ try {
50
+ request.abortSignal?.throwIfAborted();
51
+ } catch (abortReason) {
52
+ try {
53
+ await adapter.destroy?.(context);
54
+ } catch (cleanupError) {
55
+ throw new ToolExecutionAdapterCleanupError(
56
+ adapter,
57
+ context,
58
+ abortReason,
59
+ cleanupError,
60
+ );
61
+ }
62
+ throw abortReason;
63
+ }
64
+ let closed = false;
65
+ let closePromise: Promise<void> | undefined;
66
+ return {
67
+ adapterId: adapter.id,
68
+ context,
69
+ close: async () => {
70
+ if (closed) return;
71
+ if (!closePromise) {
72
+ closePromise = Promise.resolve()
73
+ .then(() => adapter.destroy?.(context))
74
+ .then(() => {
75
+ closed = true;
76
+ })
77
+ .catch((error) => {
78
+ closePromise = undefined;
79
+ throw error;
80
+ });
81
+ }
82
+ await closePromise;
83
+ },
84
+ };
85
+ };
@@ -0,0 +1,149 @@
1
+ export interface IToolPermissionRequest {
2
+ type: string;
3
+ title: string;
4
+ metadata?: Record<string, unknown>;
5
+ toolCallId?: string;
6
+ abortSignal?: AbortSignal;
7
+ }
8
+
9
+ export interface IToolInvocationOptions {
10
+ toolCallId?: string;
11
+ abortSignal?: AbortSignal;
12
+ }
13
+
14
+ export interface IToolRunOptions extends IToolInvocationOptions {
15
+ cwd?: string;
16
+ timeoutMs?: number;
17
+ /** Ordered process output. Implementations must await returned promises and abort on rejection. */
18
+ onOutput?: (stream: 'stdout' | 'stderr', chunk: string) => Promise<void> | void;
19
+ }
20
+
21
+ export interface IToolShellResult {
22
+ exitCode: number;
23
+ stdout: string;
24
+ stderr?: string;
25
+ signal?: string;
26
+ }
27
+
28
+ export interface IShellCommand {
29
+ executable: string;
30
+ args: string[];
31
+ }
32
+
33
+ export interface IToolShellContext {
34
+ run(command: string, options?: IToolRunOptions): Promise<IToolShellResult | string>;
35
+ /** Direct execution without shell parsing. Arguments are passed literally. */
36
+ runStructured?(command: IShellCommand, options?: IToolRunOptions): Promise<IToolShellResult | string>;
37
+ }
38
+
39
+ export interface IToolJobRequest {
40
+ type: 'shell' | string;
41
+ command?: IShellCommand;
42
+ cwd?: string;
43
+ timeoutMs?: number;
44
+ metadata?: Record<string, unknown>;
45
+ }
46
+
47
+ export interface IShellStartedResult {
48
+ executionId: string;
49
+ state: 'running';
50
+ }
51
+
52
+ export interface IShellFinishedResult {
53
+ executionId: string;
54
+ state: 'finished' | 'failed' | 'aborted';
55
+ exitCode: number | null;
56
+ stdout: string;
57
+ stderr: string;
58
+ signal?: string;
59
+ }
60
+
61
+ export interface IToolJobState {
62
+ executionId: string;
63
+ type: string;
64
+ state: 'running' | 'finished' | 'failed' | 'aborted';
65
+ request?: IToolJobRequest;
66
+ exitCode?: number | null;
67
+ stdout?: string;
68
+ stderr?: string;
69
+ signal?: string;
70
+ startedAt?: number;
71
+ updatedAt?: number;
72
+ finishedAt?: number;
73
+ }
74
+
75
+ export interface IToolJobHandle extends IShellStartedResult {
76
+ completion: Promise<IToolJobState>;
77
+ }
78
+
79
+ export interface IToolJobEvent {
80
+ type: 'started' | 'finished';
81
+ job: IToolJobState;
82
+ }
83
+
84
+ export interface IToolJobOutputEvent {
85
+ type: 'output';
86
+ job: IToolJobState;
87
+ stream: 'stdout' | 'stderr';
88
+ chunk: string;
89
+ sequence: number;
90
+ }
91
+
92
+ export type TToolJobEvent = IToolJobEvent | IToolJobOutputEvent;
93
+ export type TToolJobEventListener = (event: TToolJobEvent) => void | Promise<void>;
94
+
95
+ export interface IToolJobContext {
96
+ start(request: IToolJobRequest, invocationOptions?: IToolInvocationOptions): Promise<IToolJobHandle>;
97
+ get(executionId: string): Promise<IToolJobState>;
98
+ list(): Promise<IToolJobState[]>;
99
+ abort?(executionId: string): Promise<void>;
100
+ subscribe?(listener: TToolJobEventListener): () => void;
101
+ }
102
+
103
+ export interface IToolFilesystemReadOptions {
104
+ startLine?: number;
105
+ endLine?: number;
106
+ }
107
+
108
+ export interface IToolFilesystemListOptions {
109
+ recursive?: boolean;
110
+ }
111
+
112
+ export interface IToolFilesystemContext {
113
+ readFile(filePath: string, options?: IToolFilesystemReadOptions, invocationOptions?: IToolInvocationOptions): Promise<string>;
114
+ writeFile(filePath: string, content: string, invocationOptions?: IToolInvocationOptions): Promise<string | void>;
115
+ listDirectory(directoryPath: string, options?: IToolFilesystemListOptions, invocationOptions?: IToolInvocationOptions): Promise<string[] | string>;
116
+ deletePath?(targetPath: string, invocationOptions?: IToolInvocationOptions): Promise<string | void>;
117
+ }
118
+
119
+ export interface IToolBrowserRunOptions extends IToolInvocationOptions {
120
+ timeoutMs?: number;
121
+ }
122
+
123
+ export type TBrowserToolAction = 'navigate' | 'snapshot' | 'screenshot' | 'click' | 'fill' | 'press' | 'evaluate' | 'close';
124
+
125
+ export interface IBrowserToolInput {
126
+ action?: TBrowserToolAction | string;
127
+ url?: string;
128
+ selector?: string;
129
+ text?: string;
130
+ script?: string;
131
+ timeoutMs?: number;
132
+ }
133
+
134
+ export interface IToolBrowserContext {
135
+ execute(input: IBrowserToolInput, options?: IToolBrowserRunOptions): Promise<unknown>;
136
+ }
137
+
138
+ export interface IToolExecutionContext {
139
+ cwd?: string;
140
+ rootDir?: string;
141
+ additionalReadOnlyRoots?: string[];
142
+ abortSignal?: AbortSignal;
143
+ shell?: IToolShellContext;
144
+ fs?: IToolFilesystemContext;
145
+ browser?: IToolBrowserContext;
146
+ jobs?: IToolJobContext;
147
+ requestPermission?: (request: IToolPermissionRequest) => Promise<void>;
148
+ }
149
+
@@ -0,0 +1,64 @@
1
+ import type { IToolJobState } from './tool.contracts.js';
2
+
3
+ export const JOB_SNAPSHOT_SCHEMA_VERSION = 1 as const;
4
+
5
+ export interface IToolJobSnapshot {
6
+ schemaVersion: typeof JOB_SNAPSHOT_SCHEMA_VERSION;
7
+ revision: number;
8
+ updatedAt: number;
9
+ jobs: IToolJobState[];
10
+ }
11
+
12
+ export interface IToolJobStore {
13
+ load(abortSignal?: AbortSignal): Promise<IToolJobSnapshot | undefined>;
14
+ save(
15
+ jobs: readonly IToolJobState[],
16
+ expectedRevision: number,
17
+ abortSignal?: AbortSignal,
18
+ ): Promise<number>;
19
+ }
20
+
21
+ export class ToolJobStoreConflictError extends Error {
22
+ constructor(
23
+ public readonly expectedRevision: number,
24
+ public readonly actualRevision: number,
25
+ ) {
26
+ super(`Tool job store conflict: expected revision ${expectedRevision}, found ${actualRevision}.`);
27
+ this.name = 'ToolJobStoreConflictError';
28
+ }
29
+ }
30
+
31
+ export const createToolJobSnapshot = (
32
+ jobs: readonly IToolJobState[],
33
+ revision: number,
34
+ ): IToolJobSnapshot => ({
35
+ schemaVersion: JOB_SNAPSHOT_SCHEMA_VERSION,
36
+ revision,
37
+ updatedAt: Date.now(),
38
+ jobs: structuredClone([...jobs]),
39
+ });
40
+
41
+ export class InMemoryToolJobStore implements IToolJobStore {
42
+ private snapshot: IToolJobSnapshot | undefined;
43
+
44
+ public async load(abortSignal?: AbortSignal): Promise<IToolJobSnapshot | undefined> {
45
+ abortSignal?.throwIfAborted();
46
+ return this.snapshot ? structuredClone(this.snapshot) : undefined;
47
+ }
48
+
49
+ public async save(
50
+ jobs: readonly IToolJobState[],
51
+ expectedRevision: number,
52
+ abortSignal?: AbortSignal,
53
+ ): Promise<number> {
54
+ abortSignal?.throwIfAborted();
55
+ const actualRevision = this.snapshot?.revision ?? 0;
56
+ if (actualRevision !== expectedRevision) {
57
+ throw new ToolJobStoreConflictError(expectedRevision, actualRevision);
58
+ }
59
+ const revision = expectedRevision + 1;
60
+ this.snapshot = createToolJobSnapshot(jobs, revision);
61
+ return revision;
62
+ }
63
+ }
64
+