dispatch_agents 0.9.0__py3-none-any.whl

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. agentservice/__init__.py +0 -0
  2. agentservice/py.typed +0 -0
  3. agentservice/v1/__init__.py +0 -0
  4. agentservice/v1/message_pb2.py +41 -0
  5. agentservice/v1/message_pb2.pyi +22 -0
  6. agentservice/v1/message_pb2_grpc.py +4 -0
  7. agentservice/v1/request_response_pb2.py +46 -0
  8. agentservice/v1/request_response_pb2.pyi +54 -0
  9. agentservice/v1/request_response_pb2_grpc.py +4 -0
  10. agentservice/v1/service_pb2.py +43 -0
  11. agentservice/v1/service_pb2.pyi +6 -0
  12. agentservice/v1/service_pb2_grpc.py +129 -0
  13. dispatch_agents/__init__.py +281 -0
  14. dispatch_agents/agent_service.py +135 -0
  15. dispatch_agents/config.py +490 -0
  16. dispatch_agents/contrib/__init__.py +1 -0
  17. dispatch_agents/contrib/claude/__init__.py +246 -0
  18. dispatch_agents/contrib/openai/__init__.py +167 -0
  19. dispatch_agents/events.py +986 -0
  20. dispatch_agents/grpc_server.py +565 -0
  21. dispatch_agents/instrument.py +217 -0
  22. dispatch_agents/integrations/__init__.py +1 -0
  23. dispatch_agents/integrations/github/README.md +9 -0
  24. dispatch_agents/integrations/github/__init__.py +4268 -0
  25. dispatch_agents/invocation.py +25 -0
  26. dispatch_agents/llm.py +1017 -0
  27. dispatch_agents/llm_langchain.py +394 -0
  28. dispatch_agents/logging_config.py +133 -0
  29. dispatch_agents/mcp.py +266 -0
  30. dispatch_agents/memory.py +264 -0
  31. dispatch_agents/models.py +748 -0
  32. dispatch_agents/proxy/__init__.py +6 -0
  33. dispatch_agents/proxy/server.py +1137 -0
  34. dispatch_agents/proxy/sse_utils.py +76 -0
  35. dispatch_agents/py.typed +0 -0
  36. dispatch_agents/resources.py +68 -0
  37. dispatch_agents/version.py +19 -0
  38. dispatch_agents-0.9.0.dist-info/METADATA +20 -0
  39. dispatch_agents-0.9.0.dist-info/RECORD +43 -0
  40. dispatch_agents-0.9.0.dist-info/WHEEL +4 -0
  41. dispatch_agents-0.9.0.dist-info/licenses/LICENSE +191 -0
  42. dispatch_agents-0.9.0.dist-info/licenses/LICENSE-3rdparty.csv +12 -0
  43. dispatch_agents-0.9.0.dist-info/licenses/NOTICE +5 -0
File without changes
agentservice/py.typed ADDED
File without changes
File without changes
@@ -0,0 +1,41 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
3
+ # NO CHECKED-IN PROTOBUF GENCODE
4
+ # source: agentservice/v1/message.proto
5
+ # Protobuf Python Version: 6.33.5
6
+ """Generated protocol buffer code."""
7
+ from google.protobuf import descriptor as _descriptor
8
+ from google.protobuf import descriptor_pool as _descriptor_pool
9
+ from google.protobuf import runtime_version as _runtime_version
10
+ from google.protobuf import symbol_database as _symbol_database
11
+ from google.protobuf.internal import builder as _builder
12
+ _runtime_version.ValidateProtobufRuntimeVersion(
13
+ _runtime_version.Domain.PUBLIC,
14
+ 6,
15
+ 33,
16
+ 5,
17
+ '',
18
+ 'agentservice/v1/message.proto'
19
+ )
20
+ # @@protoc_insertion_point(imports)
21
+
22
+ _sym_db = _symbol_database.Default()
23
+
24
+
25
+
26
+
27
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1d\x61gentservice/v1/message.proto\x12\x0f\x61gentservice.v1\"\x9e\x01\n\x07Payload\x12\x42\n\x08metadata\x18\x01 \x03(\x0b\x32&.agentservice.v1.Payload.MetadataEntryR\x08metadata\x12\x12\n\x04\x64\x61ta\x18\x02 \x01(\x0cR\x04\x64\x61ta\x1a;\n\rMetadataEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\x0cR\x05value:\x02\x38\x01\x42\x80\x01\n\x13\x63om.agentservice.v1B\x0cMessageProtoP\x01\xa2\x02\x03\x41XX\xaa\x02\x0f\x41gentservice.V1\xca\x02\x0f\x41gentservice\\V1\xe2\x02\x1b\x41gentservice\\V1\\GPBMetadata\xea\x02\x10\x41gentservice::V1b\x06proto3')
28
+
29
+ _globals = globals()
30
+ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
31
+ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'agentservice.v1.message_pb2', _globals)
32
+ if not _descriptor._USE_C_DESCRIPTORS:
33
+ _globals['DESCRIPTOR']._loaded_options = None
34
+ _globals['DESCRIPTOR']._serialized_options = b'\n\023com.agentservice.v1B\014MessageProtoP\001\242\002\003AXX\252\002\017Agentservice.V1\312\002\017Agentservice\\V1\342\002\033Agentservice\\V1\\GPBMetadata\352\002\020Agentservice::V1'
35
+ _globals['_PAYLOAD_METADATAENTRY']._loaded_options = None
36
+ _globals['_PAYLOAD_METADATAENTRY']._serialized_options = b'8\001'
37
+ _globals['_PAYLOAD']._serialized_start=51
38
+ _globals['_PAYLOAD']._serialized_end=209
39
+ _globals['_PAYLOAD_METADATAENTRY']._serialized_start=150
40
+ _globals['_PAYLOAD_METADATAENTRY']._serialized_end=209
41
+ # @@protoc_insertion_point(module_scope)
@@ -0,0 +1,22 @@
1
+ from google.protobuf.internal import containers as _containers
2
+ from google.protobuf import descriptor as _descriptor
3
+ from google.protobuf import message as _message
4
+ from collections.abc import Mapping as _Mapping
5
+ from typing import ClassVar as _ClassVar, Optional as _Optional
6
+
7
+ DESCRIPTOR: _descriptor.FileDescriptor
8
+
9
+ class Payload(_message.Message):
10
+ __slots__ = ("metadata", "data")
11
+ class MetadataEntry(_message.Message):
12
+ __slots__ = ("key", "value")
13
+ KEY_FIELD_NUMBER: _ClassVar[int]
14
+ VALUE_FIELD_NUMBER: _ClassVar[int]
15
+ key: str
16
+ value: bytes
17
+ def __init__(self, key: _Optional[str] = ..., value: _Optional[bytes] = ...) -> None: ...
18
+ METADATA_FIELD_NUMBER: _ClassVar[int]
19
+ DATA_FIELD_NUMBER: _ClassVar[int]
20
+ metadata: _containers.ScalarMap[str, bytes]
21
+ data: bytes
22
+ def __init__(self, metadata: _Optional[_Mapping[str, bytes]] = ..., data: _Optional[bytes] = ...) -> None: ...
@@ -0,0 +1,4 @@
1
+ # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2
+ """Client and server classes corresponding to protobuf-defined services."""
3
+ import grpc
4
+
@@ -0,0 +1,46 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
3
+ # NO CHECKED-IN PROTOBUF GENCODE
4
+ # source: agentservice/v1/request_response.proto
5
+ # Protobuf Python Version: 6.33.5
6
+ """Generated protocol buffer code."""
7
+ from google.protobuf import descriptor as _descriptor
8
+ from google.protobuf import descriptor_pool as _descriptor_pool
9
+ from google.protobuf import runtime_version as _runtime_version
10
+ from google.protobuf import symbol_database as _symbol_database
11
+ from google.protobuf.internal import builder as _builder
12
+ _runtime_version.ValidateProtobufRuntimeVersion(
13
+ _runtime_version.Domain.PUBLIC,
14
+ 6,
15
+ 33,
16
+ 5,
17
+ '',
18
+ 'agentservice/v1/request_response.proto'
19
+ )
20
+ # @@protoc_insertion_point(imports)
21
+
22
+ _sym_db = _symbol_database.Default()
23
+
24
+
25
+ from agentservice.v1 import message_pb2 as agentservice_dot_v1_dot_message__pb2
26
+
27
+
28
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n&agentservice/v1/request_response.proto\x12\x0f\x61gentservice.v1\x1a\x1d\x61gentservice/v1/message.proto\"\xde\x01\n\rInvokeRequest\x12#\n\rfunction_name\x18\x01 \x01(\tR\x0c\x66unctionName\x12\x32\n\x07payload\x18\x02 \x01(\x0b\x32\x18.agentservice.v1.PayloadR\x07payload\x12\x10\n\x03uid\x18\x03 \x01(\tR\x03uid\x12\x19\n\x08trace_id\x18\x04 \x01(\tR\x07traceId\x12\x0e\n\x02ts\x18\x06 \x01(\tR\x02ts\x12\x14\n\x05topic\x18\x07 \x01(\tR\x05topic\x12!\n\x0cmessage_type\x18\x08 \x01(\tR\x0bmessageType\"]\n\x0eInvokeResponse\x12\x30\n\x06result\x18\x01 \x01(\x0b\x32\x18.agentservice.v1.PayloadR\x06result\x12\x19\n\x08is_error\x18\x02 \x01(\x08R\x07isError\"\x14\n\x12HealthCheckRequest\"\xf3\x01\n\x13HealthCheckResponse\x12J\n\x06status\x18\x01 \x01(\x0e\x32\x32.agentservice.v1.HealthCheckResponse.ServingStatusR\x06status\"\x8f\x01\n\rServingStatus\x12\x1e\n\x1aSERVING_STATUS_UNSPECIFIED\x10\x00\x12\x1a\n\x16SERVING_STATUS_SERVING\x10\x01\x12\x1e\n\x1aSERVING_STATUS_NOT_SERVING\x10\x02\x12\"\n\x1eSERVING_STATUS_SERVICE_UNKNOWN\x10\x03\x42\x88\x01\n\x13\x63om.agentservice.v1B\x14RequestResponseProtoP\x01\xa2\x02\x03\x41XX\xaa\x02\x0f\x41gentservice.V1\xca\x02\x0f\x41gentservice\\V1\xe2\x02\x1b\x41gentservice\\V1\\GPBMetadata\xea\x02\x10\x41gentservice::V1b\x06proto3')
29
+
30
+ _globals = globals()
31
+ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
32
+ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'agentservice.v1.request_response_pb2', _globals)
33
+ if not _descriptor._USE_C_DESCRIPTORS:
34
+ _globals['DESCRIPTOR']._loaded_options = None
35
+ _globals['DESCRIPTOR']._serialized_options = b'\n\023com.agentservice.v1B\024RequestResponseProtoP\001\242\002\003AXX\252\002\017Agentservice.V1\312\002\017Agentservice\\V1\342\002\033Agentservice\\V1\\GPBMetadata\352\002\020Agentservice::V1'
36
+ _globals['_INVOKEREQUEST']._serialized_start=91
37
+ _globals['_INVOKEREQUEST']._serialized_end=313
38
+ _globals['_INVOKERESPONSE']._serialized_start=315
39
+ _globals['_INVOKERESPONSE']._serialized_end=408
40
+ _globals['_HEALTHCHECKREQUEST']._serialized_start=410
41
+ _globals['_HEALTHCHECKREQUEST']._serialized_end=430
42
+ _globals['_HEALTHCHECKRESPONSE']._serialized_start=433
43
+ _globals['_HEALTHCHECKRESPONSE']._serialized_end=676
44
+ _globals['_HEALTHCHECKRESPONSE_SERVINGSTATUS']._serialized_start=533
45
+ _globals['_HEALTHCHECKRESPONSE_SERVINGSTATUS']._serialized_end=676
46
+ # @@protoc_insertion_point(module_scope)
@@ -0,0 +1,54 @@
1
+ from agentservice.v1 import message_pb2 as _message_pb2
2
+ from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper
3
+ from google.protobuf import descriptor as _descriptor
4
+ from google.protobuf import message as _message
5
+ from collections.abc import Mapping as _Mapping
6
+ from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union
7
+
8
+ DESCRIPTOR: _descriptor.FileDescriptor
9
+
10
+ class InvokeRequest(_message.Message):
11
+ __slots__ = ("function_name", "payload", "uid", "trace_id", "ts", "topic", "message_type")
12
+ FUNCTION_NAME_FIELD_NUMBER: _ClassVar[int]
13
+ PAYLOAD_FIELD_NUMBER: _ClassVar[int]
14
+ UID_FIELD_NUMBER: _ClassVar[int]
15
+ TRACE_ID_FIELD_NUMBER: _ClassVar[int]
16
+ TS_FIELD_NUMBER: _ClassVar[int]
17
+ TOPIC_FIELD_NUMBER: _ClassVar[int]
18
+ MESSAGE_TYPE_FIELD_NUMBER: _ClassVar[int]
19
+ function_name: str
20
+ payload: _message_pb2.Payload
21
+ uid: str
22
+ trace_id: str
23
+ ts: str
24
+ topic: str
25
+ message_type: str
26
+ def __init__(self, function_name: _Optional[str] = ..., payload: _Optional[_Union[_message_pb2.Payload, _Mapping]] = ..., uid: _Optional[str] = ..., trace_id: _Optional[str] = ..., ts: _Optional[str] = ..., topic: _Optional[str] = ..., message_type: _Optional[str] = ...) -> None: ...
27
+
28
+ class InvokeResponse(_message.Message):
29
+ __slots__ = ("result", "is_error")
30
+ RESULT_FIELD_NUMBER: _ClassVar[int]
31
+ IS_ERROR_FIELD_NUMBER: _ClassVar[int]
32
+ result: _message_pb2.Payload
33
+ is_error: bool
34
+ def __init__(self, result: _Optional[_Union[_message_pb2.Payload, _Mapping]] = ..., is_error: _Optional[bool] = ...) -> None: ...
35
+
36
+ class HealthCheckRequest(_message.Message):
37
+ __slots__ = ()
38
+ def __init__(self) -> None: ...
39
+
40
+ class HealthCheckResponse(_message.Message):
41
+ __slots__ = ("status",)
42
+ class ServingStatus(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
43
+ __slots__ = ()
44
+ SERVING_STATUS_UNSPECIFIED: _ClassVar[HealthCheckResponse.ServingStatus]
45
+ SERVING_STATUS_SERVING: _ClassVar[HealthCheckResponse.ServingStatus]
46
+ SERVING_STATUS_NOT_SERVING: _ClassVar[HealthCheckResponse.ServingStatus]
47
+ SERVING_STATUS_SERVICE_UNKNOWN: _ClassVar[HealthCheckResponse.ServingStatus]
48
+ SERVING_STATUS_UNSPECIFIED: HealthCheckResponse.ServingStatus
49
+ SERVING_STATUS_SERVING: HealthCheckResponse.ServingStatus
50
+ SERVING_STATUS_NOT_SERVING: HealthCheckResponse.ServingStatus
51
+ SERVING_STATUS_SERVICE_UNKNOWN: HealthCheckResponse.ServingStatus
52
+ STATUS_FIELD_NUMBER: _ClassVar[int]
53
+ status: HealthCheckResponse.ServingStatus
54
+ def __init__(self, status: _Optional[_Union[HealthCheckResponse.ServingStatus, str]] = ...) -> None: ...
@@ -0,0 +1,4 @@
1
+ # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2
+ """Client and server classes corresponding to protobuf-defined services."""
3
+ import grpc
4
+
@@ -0,0 +1,43 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
3
+ # NO CHECKED-IN PROTOBUF GENCODE
4
+ # source: agentservice/v1/service.proto
5
+ # Protobuf Python Version: 6.33.5
6
+ """Generated protocol buffer code."""
7
+ from google.protobuf import descriptor as _descriptor
8
+ from google.protobuf import descriptor_pool as _descriptor_pool
9
+ from google.protobuf import runtime_version as _runtime_version
10
+ from google.protobuf import symbol_database as _symbol_database
11
+ from google.protobuf.internal import builder as _builder
12
+ _runtime_version.ValidateProtobufRuntimeVersion(
13
+ _runtime_version.Domain.PUBLIC,
14
+ 6,
15
+ 33,
16
+ 5,
17
+ '',
18
+ 'agentservice/v1/service.proto'
19
+ )
20
+ # @@protoc_insertion_point(imports)
21
+
22
+ _sym_db = _symbol_database.Default()
23
+
24
+
25
+ from agentservice.v1 import request_response_pb2 as agentservice_dot_v1_dot_request__response__pb2
26
+ from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2
27
+
28
+
29
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1d\x61gentservice/v1/service.proto\x12\x0f\x61gentservice.v1\x1a&agentservice/v1/request_response.proto\x1a\x1cgoogle/api/annotations.proto2\xf2\x01\n\x0c\x41gentService\x12j\n\x06Invoke\x12\x1e.agentservice.v1.InvokeRequest\x1a\x1f.agentservice.v1.InvokeResponse\"\x1f\x82\xd3\xe4\x93\x02\x19\"\x14/api/v1/agent/invoke:\x01*\x12v\n\x0bHealthCheck\x12#.agentservice.v1.HealthCheckRequest\x1a$.agentservice.v1.HealthCheckResponse\"\x1c\x82\xd3\xe4\x93\x02\x16\x12\x14/api/v1/agent/healthB\x80\x01\n\x13\x63om.agentservice.v1B\x0cServiceProtoP\x01\xa2\x02\x03\x41XX\xaa\x02\x0f\x41gentservice.V1\xca\x02\x0f\x41gentservice\\V1\xe2\x02\x1b\x41gentservice\\V1\\GPBMetadata\xea\x02\x10\x41gentservice::V1b\x06proto3')
30
+
31
+ _globals = globals()
32
+ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
33
+ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'agentservice.v1.service_pb2', _globals)
34
+ if not _descriptor._USE_C_DESCRIPTORS:
35
+ _globals['DESCRIPTOR']._loaded_options = None
36
+ _globals['DESCRIPTOR']._serialized_options = b'\n\023com.agentservice.v1B\014ServiceProtoP\001\242\002\003AXX\252\002\017Agentservice.V1\312\002\017Agentservice\\V1\342\002\033Agentservice\\V1\\GPBMetadata\352\002\020Agentservice::V1'
37
+ _globals['_AGENTSERVICE'].methods_by_name['Invoke']._loaded_options = None
38
+ _globals['_AGENTSERVICE'].methods_by_name['Invoke']._serialized_options = b'\202\323\344\223\002\031\"\024/api/v1/agent/invoke:\001*'
39
+ _globals['_AGENTSERVICE'].methods_by_name['HealthCheck']._loaded_options = None
40
+ _globals['_AGENTSERVICE'].methods_by_name['HealthCheck']._serialized_options = b'\202\323\344\223\002\026\022\024/api/v1/agent/health'
41
+ _globals['_AGENTSERVICE']._serialized_start=121
42
+ _globals['_AGENTSERVICE']._serialized_end=363
43
+ # @@protoc_insertion_point(module_scope)
@@ -0,0 +1,6 @@
1
+ from agentservice.v1 import request_response_pb2 as _request_response_pb2
2
+ from google.api import annotations_pb2 as _annotations_pb2
3
+ from google.protobuf import descriptor as _descriptor
4
+ from typing import ClassVar as _ClassVar
5
+
6
+ DESCRIPTOR: _descriptor.FileDescriptor
@@ -0,0 +1,129 @@
1
+ # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2
+ """Client and server classes corresponding to protobuf-defined services."""
3
+ import grpc
4
+
5
+ from agentservice.v1 import request_response_pb2 as agentservice_dot_v1_dot_request__response__pb2
6
+
7
+
8
+ class AgentServiceStub(object):
9
+ """AgentService provides a simple RPC interface that mirrors the agent_sidecar
10
+ temporal worker's activity API for invoking agents and awaiting results.
11
+ """
12
+
13
+ def __init__(self, channel):
14
+ """Constructor.
15
+
16
+ Args:
17
+ channel: A grpc.Channel.
18
+ """
19
+ self.Invoke = channel.unary_unary(
20
+ '/agentservice.v1.AgentService/Invoke',
21
+ request_serializer=agentservice_dot_v1_dot_request__response__pb2.InvokeRequest.SerializeToString,
22
+ response_deserializer=agentservice_dot_v1_dot_request__response__pb2.InvokeResponse.FromString,
23
+ _registered_method=True)
24
+ self.HealthCheck = channel.unary_unary(
25
+ '/agentservice.v1.AgentService/HealthCheck',
26
+ request_serializer=agentservice_dot_v1_dot_request__response__pb2.HealthCheckRequest.SerializeToString,
27
+ response_deserializer=agentservice_dot_v1_dot_request__response__pb2.HealthCheckResponse.FromString,
28
+ _registered_method=True)
29
+
30
+
31
+ class AgentServiceServicer(object):
32
+ """AgentService provides a simple RPC interface that mirrors the agent_sidecar
33
+ temporal worker's activity API for invoking agents and awaiting results.
34
+ """
35
+
36
+ def Invoke(self, request, context):
37
+ """Invoke invokes an agent and waits for the result.
38
+ This mirrors the temporal activity API used by the agent_sidecar worker.
39
+ """
40
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
41
+ context.set_details('Method not implemented!')
42
+ raise NotImplementedError('Method not implemented!')
43
+
44
+ def HealthCheck(self, request, context):
45
+ """Check performs a health check on the agent.
46
+ """
47
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
48
+ context.set_details('Method not implemented!')
49
+ raise NotImplementedError('Method not implemented!')
50
+
51
+
52
+ def add_AgentServiceServicer_to_server(servicer, server):
53
+ rpc_method_handlers = {
54
+ 'Invoke': grpc.unary_unary_rpc_method_handler(
55
+ servicer.Invoke,
56
+ request_deserializer=agentservice_dot_v1_dot_request__response__pb2.InvokeRequest.FromString,
57
+ response_serializer=agentservice_dot_v1_dot_request__response__pb2.InvokeResponse.SerializeToString,
58
+ ),
59
+ 'HealthCheck': grpc.unary_unary_rpc_method_handler(
60
+ servicer.HealthCheck,
61
+ request_deserializer=agentservice_dot_v1_dot_request__response__pb2.HealthCheckRequest.FromString,
62
+ response_serializer=agentservice_dot_v1_dot_request__response__pb2.HealthCheckResponse.SerializeToString,
63
+ ),
64
+ }
65
+ generic_handler = grpc.method_handlers_generic_handler(
66
+ 'agentservice.v1.AgentService', rpc_method_handlers)
67
+ server.add_generic_rpc_handlers((generic_handler,))
68
+ server.add_registered_method_handlers('agentservice.v1.AgentService', rpc_method_handlers)
69
+
70
+
71
+ # This class is part of an EXPERIMENTAL API.
72
+ class AgentService(object):
73
+ """AgentService provides a simple RPC interface that mirrors the agent_sidecar
74
+ temporal worker's activity API for invoking agents and awaiting results.
75
+ """
76
+
77
+ @staticmethod
78
+ def Invoke(request,
79
+ target,
80
+ options=(),
81
+ channel_credentials=None,
82
+ call_credentials=None,
83
+ insecure=False,
84
+ compression=None,
85
+ wait_for_ready=None,
86
+ timeout=None,
87
+ metadata=None):
88
+ return grpc.experimental.unary_unary(
89
+ request,
90
+ target,
91
+ '/agentservice.v1.AgentService/Invoke',
92
+ agentservice_dot_v1_dot_request__response__pb2.InvokeRequest.SerializeToString,
93
+ agentservice_dot_v1_dot_request__response__pb2.InvokeResponse.FromString,
94
+ options,
95
+ channel_credentials,
96
+ insecure,
97
+ call_credentials,
98
+ compression,
99
+ wait_for_ready,
100
+ timeout,
101
+ metadata,
102
+ _registered_method=True)
103
+
104
+ @staticmethod
105
+ def HealthCheck(request,
106
+ target,
107
+ options=(),
108
+ channel_credentials=None,
109
+ call_credentials=None,
110
+ insecure=False,
111
+ compression=None,
112
+ wait_for_ready=None,
113
+ timeout=None,
114
+ metadata=None):
115
+ return grpc.experimental.unary_unary(
116
+ request,
117
+ target,
118
+ '/agentservice.v1.AgentService/HealthCheck',
119
+ agentservice_dot_v1_dot_request__response__pb2.HealthCheckRequest.SerializeToString,
120
+ agentservice_dot_v1_dot_request__response__pb2.HealthCheckResponse.FromString,
121
+ options,
122
+ channel_credentials,
123
+ insecure,
124
+ call_credentials,
125
+ compression,
126
+ wait_for_ready,
127
+ timeout,
128
+ metadata,
129
+ _registered_method=True)
@@ -0,0 +1,281 @@
1
+ import os
2
+ import sys
3
+ import tempfile
4
+ from pathlib import Path
5
+
6
+ # Add SDK root to Python path so generated protobuf files can import
7
+ # using their package structure (e.g., "from agentservice.v1 import ...")
8
+ _sdk_root = str(Path(__file__).parent.parent)
9
+ if _sdk_root not in sys.path:
10
+ sys.path.insert(0, _sdk_root)
11
+
12
+
13
+ def get_data_dir() -> Path:
14
+ """Get the data directory for persistent storage.
15
+
16
+ In production, this returns /data (EFS mount point).
17
+ In local dev mode (`dispatch agent dev`), this returns the mock data directory
18
+ set by DISPATCH_DEV_DATA_DIR environment variable.
19
+
20
+ Returns:
21
+ Path to the data directory
22
+
23
+ Example:
24
+ from dispatch_agents import get_data_dir
25
+
26
+ DATA_DIR = get_data_dir()
27
+ my_file = DATA_DIR / "myfile.txt"
28
+ my_file.write_text("hello")
29
+ """
30
+ dev_data_dir = os.environ.get("DISPATCH_DEV_DATA_DIR")
31
+ if dev_data_dir:
32
+ # In dev mode, use the mock data directory
33
+ return Path(dev_data_dir) / "data"
34
+ # In production, use the EFS mount at /data
35
+ return Path("/data")
36
+
37
+
38
+ # =============================================================================
39
+ # Dev Mode Audit Hook - Restricts writes to allowed directories
40
+ # =============================================================================
41
+
42
+
43
+ class DisallowedWriteError(Exception):
44
+ """Raised when an agent attempts to write outside allowed directories in dev mode."""
45
+
46
+ pass
47
+
48
+
49
+ _audit_hook_blocked: set[str] = set() # Track blocked paths to avoid duplicate errors
50
+ _audit_hook_allowed_prefixes: list[str] = [] # Computed once at startup
51
+
52
+
53
+ def _init_allowed_prefixes() -> list[str]:
54
+ """Compute the list of allowed write prefixes for dev mode.
55
+
56
+ Allowed locations:
57
+ 1. get_data_dir() - the mock data directory
58
+ 2. /tmp - temporary files (and system temp directory)
59
+ 3. Agent folder - where the agent code lives (derived from DISPATCH_DEV_DATA_DIR)
60
+
61
+ All paths are resolved to their canonical form to handle symlinks
62
+ (e.g., macOS /var -> /private/var).
63
+ """
64
+ allowed = []
65
+
66
+ def resolve_path(p: str) -> str:
67
+ """Resolve path to canonical form, handling symlinks."""
68
+ try:
69
+ return str(Path(p).resolve())
70
+ except (OSError, ValueError):
71
+ return p
72
+
73
+ # 1. The data directory from get_data_dir()
74
+ allowed.append(resolve_path(str(get_data_dir())))
75
+
76
+ # 2. Temp directories - include both standard paths and the system's actual temp
77
+ # Resolve all paths to handle macOS symlinks (/var -> /private/var)
78
+ allowed.append(resolve_path("/tmp"))
79
+ allowed.append(resolve_path("/var/tmp"))
80
+ # macOS temp dirs (may be redundant after resolution, but included for safety)
81
+ allowed.append(resolve_path("/private/tmp"))
82
+ allowed.append(resolve_path("/private/var/tmp"))
83
+ # System temp directory (handles macOS /var/folders/... and other platforms)
84
+ allowed.append(resolve_path(tempfile.gettempdir()))
85
+
86
+ # 3. Agent folder - DISPATCH_DEV_DATA_DIR is {agent_path}/.dispatch/dev-data
87
+ dev_data_dir = os.environ.get("DISPATCH_DEV_DATA_DIR", "")
88
+ if dev_data_dir:
89
+ # Go up two levels: dev-data -> .dispatch -> agent_folder
90
+ agent_folder = str(Path(dev_data_dir).parent.parent)
91
+ allowed.append(resolve_path(agent_folder))
92
+
93
+ return allowed
94
+
95
+
96
+ def _dev_mode_audit_hook(event: str, args: tuple) -> None:
97
+ """Block file operations that target paths outside allowed directories.
98
+
99
+ In dev mode, agents should only write to:
100
+ - get_data_dir() (mock persistent storage)
101
+ - /tmp (temporary files)
102
+ - The agent's own folder
103
+
104
+ This protects the developer's machine from accidental writes to arbitrary locations.
105
+ Use --allow-arbitrary-writes flag or DISPATCH_ALLOW_ARBITRARY_WRITES=1 to disable.
106
+ """
107
+ # Only care about file open operations that could write
108
+ if event != "open":
109
+ return
110
+
111
+ if not args or len(args) < 2:
112
+ return
113
+
114
+ raw_path = str(args[0])
115
+ mode = args[1] if len(args) > 1 else ""
116
+
117
+ # Only check write operations (mode contains 'w', 'a', 'x', or '+')
118
+ # Skip read-only operations
119
+ if isinstance(mode, str):
120
+ if "w" not in mode and "a" not in mode and "x" not in mode and "+" not in mode:
121
+ return
122
+ elif isinstance(mode, int):
123
+ # For integer flags, check if it's a write mode
124
+ # os.O_WRONLY = 1, os.O_RDWR = 2, os.O_APPEND = 1024, os.O_CREAT = 512
125
+ import os as os_module
126
+
127
+ write_flags = getattr(os_module, "O_WRONLY", 1) | getattr(
128
+ os_module, "O_RDWR", 2
129
+ )
130
+ if not (mode & write_flags):
131
+ return
132
+
133
+ # Resolve relative paths to absolute (e.g., "../test" -> "/absolute/path/test")
134
+ # This ensures path traversal attacks like "../../../etc/passwd" are caught
135
+ try:
136
+ path = str(Path(raw_path).resolve())
137
+ except (OSError, ValueError):
138
+ # If path resolution fails, use the raw path
139
+ path = raw_path
140
+
141
+ # Check if path is within any allowed prefix
142
+ for prefix in _audit_hook_allowed_prefixes:
143
+ if path.startswith(prefix):
144
+ return # Allowed
145
+
146
+ # Track whether we've already shown the detailed error for this path
147
+ # (to reduce noise), but ALWAYS raise the error to block the write
148
+ show_details = path not in _audit_hook_blocked
149
+ _audit_hook_blocked.add(path)
150
+
151
+ if show_details:
152
+ allowed_locations = ", ".join(_audit_hook_allowed_prefixes[:3]) # Show first 3
153
+ raise DisallowedWriteError(
154
+ f"Write operation to '{path}' blocked - outside allowed directories.\n"
155
+ f"In dev mode, writes are only allowed to: {allowed_locations}\n"
156
+ "Use get_data_dir() for persistent storage.\n"
157
+ "To disable this check, run with: dispatch agent dev --allow-arbitrary-writes"
158
+ )
159
+ else:
160
+ # Subsequent attempts to the same path: still block, but shorter message
161
+ raise DisallowedWriteError(f"Write to '{path}' blocked (repeated attempt)")
162
+
163
+
164
+ from .agent_service import AgentServiceClient
165
+ from .events import (
166
+ HANDLER_METADATA,
167
+ REGISTERED_HANDLERS,
168
+ TOPIC_HANDLERS,
169
+ AsyncHandler,
170
+ BasePayload,
171
+ HandlerMetadata,
172
+ dispatch_message,
173
+ emit_event,
174
+ fn,
175
+ get_current_invocation_id,
176
+ get_current_parent_id,
177
+ get_current_trace_id,
178
+ get_handler_metadata,
179
+ get_handler_schemas,
180
+ get_invocation_id_for_trace,
181
+ init,
182
+ invoke,
183
+ on,
184
+ run_init_hook,
185
+ )
186
+ from .invocation import InvocationStatus
187
+ from .llm import LLMFunctionCall, LLMToolCall, extra_headers, get_extra_llm_headers
188
+ from .mcp import McpHttpServerConfig, get_mcp_client, get_mcp_servers_config
189
+ from .memory import MemoryClient, memory
190
+ from .models import (
191
+ Agent,
192
+ AgentContainerStatus,
193
+ BaseMessage,
194
+ FeedbackSentiment,
195
+ FeedbackType,
196
+ FunctionMessage,
197
+ JsonSchema,
198
+ KVGetResponse,
199
+ LLMCallMessage,
200
+ MemoryWriteResponse,
201
+ Message,
202
+ ScheduleMessage,
203
+ SessionGetResponse,
204
+ StrictBaseModel,
205
+ TopicMessage,
206
+ )
207
+
208
+ __all__ = [
209
+ # storage and dev mode isolation
210
+ "get_data_dir",
211
+ "DisallowedWriteError",
212
+ # agent service
213
+ "AgentServiceClient",
214
+ # events - decorators and client functions
215
+ "on",
216
+ "init",
217
+ "fn",
218
+ "invoke",
219
+ "dispatch_message",
220
+ "emit_event",
221
+ "run_init_hook",
222
+ # events - context and metadata
223
+ "get_current_trace_id",
224
+ "get_current_invocation_id",
225
+ "get_current_parent_id",
226
+ "get_invocation_id_for_trace",
227
+ "get_handler_schemas",
228
+ "get_handler_metadata",
229
+ # events - registries and types
230
+ "REGISTERED_HANDLERS", # handler_name -> AsyncHandler
231
+ "HANDLER_METADATA", # handler_name -> HandlerMetadata
232
+ "TOPIC_HANDLERS", # topic -> list of handler_names
233
+ "AsyncHandler", # Type alias for handler functions
234
+ "HandlerMetadata", # Pydantic model for handler metadata
235
+ "BasePayload",
236
+ # invocation
237
+ "InvocationStatus",
238
+ # models - Message types (discriminated union pattern)
239
+ "BaseMessage",
240
+ "TopicMessage",
241
+ "FunctionMessage",
242
+ "ScheduleMessage",
243
+ "LLMCallMessage",
244
+ "Message", # Union type alias (TopicMessage | FunctionMessage | ScheduleMessage)
245
+ "Agent",
246
+ "AgentContainerStatus",
247
+ "StrictBaseModel",
248
+ # memory
249
+ "MemoryClient",
250
+ "memory",
251
+ "MemoryWriteResponse",
252
+ "KVGetResponse",
253
+ "SessionGetResponse",
254
+ # type aliases
255
+ "JsonSchema",
256
+ # feedback types
257
+ "FeedbackType",
258
+ "FeedbackSentiment",
259
+ # mcp
260
+ "get_mcp_client",
261
+ "get_mcp_servers_config",
262
+ "McpHttpServerConfig",
263
+ # llm
264
+ "LLMFunctionCall",
265
+ "LLMToolCall",
266
+ "extra_headers",
267
+ "get_extra_llm_headers",
268
+ ]
269
+
270
+ # =============================================================================
271
+ # Install Dev Mode Audit Hook (MUST be after all imports to avoid blocking .pyc writes)
272
+ # =============================================================================
273
+
274
+ # Install the audit hook only in dev mode, unless arbitrary writes are explicitly allowed
275
+ if os.environ.get("DISPATCH_DEV_DATA_DIR") and not os.environ.get(
276
+ "DISPATCH_ALLOW_ARBITRARY_WRITES"
277
+ ):
278
+ # IMPORTANT: Initialize allowed prefixes BEFORE installing the hook.
279
+ # This avoids recursive calls when tempfile.gettempdir() triggers file operations.
280
+ _audit_hook_allowed_prefixes = _init_allowed_prefixes()
281
+ sys.addaudithook(_dev_mode_audit_hook)