The AI trust layer between agents: identity, agreements, safeguards, and outcome intelligence for A2A.

Viewing files at commit 867f032d6ce0Commit detailsView latest

connectors/botpress/src/checkpoint.ts 78 lines · 3.3 KB · typescript
1
export type StructuredCheckpoint = {
2
  state: 'active' | 'blocked' | 'ready_to_finalize' | 'done' | 'cancelled';
3
  progress: number;
4
  criteriaMet: string[];
5
  criteriaRemaining: string[];
6
  blockerCodes: string[];
7
  topicStatus: 'in_scope' | 'drifting' | 'changed';
8
  expectedRemainingTurns?: number;
9
  needsHuman: boolean;
10
  confidence: number;
11
};
12
13
const strings = (value: unknown, limit: number, maxLength: number) =>
14
  (Array.isArray(value) ? value : [])
15
    .filter((item): item is string => typeof item === 'string' && item.trim().length > 0)
16
    .slice(0, limit)
17
    .map((item) => item.trim().slice(0, maxLength));
18
19
const checkpointJson = (value: string) => {
20
  const marker = value.indexOf('OPENCLASP_CHECKPOINT');
21
  const source = marker >= 0 ? value.slice(marker + 'OPENCLASP_CHECKPOINT'.length) : value;
22
  const start = source.indexOf('{');
23
  if (start < 0) throw new Error('Checkpoint JSON object is missing');
24
  let depth = 0;
25
  let quoted = false;
26
  let escaped = false;
27
  for (let index = start; index < source.length; index += 1) {
28
    const character = source[index]!;
29
    if (quoted) {
30
      if (escaped) escaped = false;
31
      else if (character === '\\') escaped = true;
32
      else if (character === '"') quoted = false;
33
      continue;
34
    }
35
    if (character === '"') quoted = true;
36
    else if (character === '{') depth += 1;
37
    else if (character === '}' && --depth === 0) return JSON.parse(source.slice(start, index + 1));
38
  }
39
  throw new Error('Checkpoint JSON object is incomplete');
40
};
41
42
export function parseCheckpoint(text: string): StructuredCheckpoint {
43
  const value = checkpointJson(text) as Record<string, unknown>;
44
  const states = ['active', 'blocked', 'ready_to_finalize', 'done', 'cancelled'] as const;
45
  const topics = ['in_scope', 'drifting', 'changed'] as const;
46
  if (!states.includes(value.state as (typeof states)[number]))
47
    throw new Error('Checkpoint state is invalid');
48
  if (!topics.includes(value.topicStatus as (typeof topics)[number]))
49
    throw new Error('Checkpoint topic status is invalid');
50
  if (typeof value.progress !== 'number' || value.progress < 0 || value.progress > 1)
51
    throw new Error('Checkpoint progress is invalid');
52
  if (typeof value.confidence !== 'number' || value.confidence < 0 || value.confidence > 1)
53
    throw new Error('Checkpoint confidence is invalid');
54
  if (typeof value.needsHuman !== 'boolean') throw new Error('Checkpoint needsHuman is invalid');
55
  if (
56
    value.expectedRemainingTurns !== undefined &&
57
    (typeof value.expectedRemainingTurns !== 'number' ||
58
      !Number.isInteger(value.expectedRemainingTurns) ||
59
      value.expectedRemainingTurns < 0 ||
60
      value.expectedRemainingTurns > 1000)
61
  )
62
    throw new Error('Checkpoint expectedRemainingTurns is invalid');
63
  return {
64
    state: value.state as StructuredCheckpoint['state'],
65
    progress: value.progress,
66
    criteriaMet: strings(value.criteriaMet, 100, 1000),
67
    criteriaRemaining: strings(value.criteriaRemaining, 100, 1000),
68
    blockerCodes: strings(value.blockerCodes, 32, 64).filter((item) =>
69
      /^[a-zA-Z0-9][a-zA-Z0-9_.:-]{0,63}$/.test(item),
70
    ),
71
    topicStatus: value.topicStatus as StructuredCheckpoint['topicStatus'],
72
    ...(value.expectedRemainingTurns !== undefined
73
      ? { expectedRemainingTurns: value.expectedRemainingTurns as number }
74
      : {}),
75
    needsHuman: value.needsHuman,
76
    confidence: value.confidence,
77
  };
78
}