fix(docs): (DO NOT MERGE) strands integration - #29

Closed
namrataghadi-galileo wants to merge 29 commits into
mainfrom
feature/strands-integration
Closed

fix(docs): (DO NOT MERGE) strands integration#29
namrataghadi-galileo wants to merge 29 commits into
mainfrom
feature/strands-integration

Conversation

@namrataghadi-galileo

@namrataghadi-galileonamrataghadi-galileo commented Feb 13, 2026

Copy link
Copy Markdown
Contributor

Strands Integration Example
Demonstrates automatic safety controls for AWS Strands agents using a reusable hook pattern - no code decorators needed.

What's Included

Core Integration

  • AgentControlHook - Reusable hook class that intercepts Strands agent events and enforces controls
  • Automatic tool name extraction from Strands events for tool-specific controls
  • Support for both pre and post-stage evaluation across LLM calls and tool executions

Interactive Demo (interactive_demo/)

  • Customer support agent with Streamlit UI
  • Real-time PII detection (SSN, email, credit card blocking)
  • SQL injection prevention for database queries
  • Shows control violations with user-friendly error messages

Steering Demo (steering_demo/)

  • Banking email agent with PII redaction
  • Combines AgentControl detection + Strands Steering correction
  • Demonstrates layered governance: detect sensitive data, then intelligently redact it
  • Shows integration between two safety systems

Hook-Based Architecture

  • Attaches to Strands agents via the hooks parameter
  • Intercepts BeforeToolCallEvent, AfterModelCallEvent, and other Strands lifecycle events
  • Automatically constructs Step payloads and evaluates controls server-side
  • No modification to agent code or tool definitions required

@namrataghadi-galileo
namrataghadi-galileo changed the base branch from main to feature/54252-step_name-as_parameterFebruary 13, 2026 22:41
@codecov

codecovBot commented Feb 13, 2026

Copy link
Copy Markdown

The author of this PR, namrataghadi-galileo, is not an activated member of this organization on Codecov.
Please activate this user on Codecov to display this PR comment.
Coverage data is still being uploaded to Codecov.io for purposes of overall coverage calculations.
Please don't hesitate to email us at support@codecov.io with any questions.

Comment threadexamples/strands_integration/interactive_demo/interactive_support_demo.py Outdated
Base automatically changed from feature/54252-step_name-as_parameter to mainFebruary 17, 2026 17:00
@namrataghadi-galileo
namrataghadi-galileo changed the base branch from main to feature/add-stage-overrideFebruary 19, 2026 00:20
@namrataghadi-galileo
namrataghadi-galileo changed the base branch from feature/add-stage-override to mainFebruary 21, 2026 02:52
Comment threadsdks/python/src/agent_control/evaluation.py Outdated
Comment threadsdks/python/src/agent_control/evaluation.py Outdated
Comment threadsdks/python/src/agent_control/evaluation.py Outdated
Comment threadsdks/python/src/agent_control/evaluation.py Outdated

@lan17lan17 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM overall, just a few remaining nits, but i love the more simplified version of evaluate :)

async def evaluate_controls(
step_name: str,
*,
input: Any = None,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be Any | None? not sure if Any covers None

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, in what situations would this be null?

Same question for the output.

I suppose it would only be the case if there's no args to a tool call and a void output?

context: dict[str, Any] | None = None,
step_type: Literal["tool", "llm"] = "llm",
stage: Literal["pre", "post"] = "pre",
agent_uuid: str | UUID,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wonder if we should get rid of it since im getting rid of it here: #44

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will have to uptake it once your PR is merged.

output_text = ""

# BeforeInvocationEvent - has messages attribute with user input
if hasattr(event, "messages") and event.messages:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explicit event type checks would be preferable IMHO - for type safety purposes and readability

Same for below for tools

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure.

# selected_tool is an AgentTool object with name attribute
if hasattr(event.selected_tool, "name"):
tool_name = event.selected_tool.name
elif hasattr(event.selected_tool, "__name__"):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you see a case where you got something that wasn't AgentTool? Same question for below and "get"

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type check refactor takes care of this redundancy

if isinstance(event.tool_use, dict):
tool_name = event.tool_use.get("toolName", "unknown-tool")
elif hasattr(event.tool_use, "get"):
tool_name = event.tool_use.get("toolName", "unknown-tool")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar question to the need for "get" here

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed. The original code had overly defensive hasattr() checks that don't correspond to any real case in Strands. Refactored code correctly removes these unnecessary checks and uses the actual types

print(f"🔍 Extracted tool name: {tool_name}")

# Extract input (pre-stage) or output (post-stage)
if event_phase == "pre":

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you did an isinstance check for BeforeToolCall and AfterToolCall, I think you could even omit the event_phase here?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switching to an isinstance check let us remove a lot of code — great catch. This is handled there now.

content = content.content

# List of content blocks
if isinstance(content, list):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you ever want anything other than plain text?

Our AgentResult also stringifies structured output and citation blocks

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hooks intercept at a lower level (model/tool calls) BEFORE AgentResult is constructed. What we receive from the events is:

Message.content: list[ContentBlock]
ToolResult.content: list[ToolResultContent]
invocation_state values: Any (strings, dicts, etc.)

So we need more than plain text—thanks for flagging it. We now extract text, citations, tool info, and JSON from ContentBlock/ToolResultContent TypedDicts for control inspection, since those fields can contain content that needs policy enforcement.

@namrataghadi-galileonamrataghadi-galileo changed the title fix(docs): strands integrationfix(docs): (DO NOT MERGE) strands integrationMar 5, 2026
@namrataghadi-galileo
namrataghadi-galileo deleted the feature/strands-integration branch March 10, 2026 05:00
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@namrataghadi-galileo@yashsheth46@zastrowm@lan17
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(docs): (DO NOT MERGE) strands integration - #29

Closed
namrataghadi-galileo wants to merge 29 commits into
mainfrom
feature/strands-integration
Closed

fix(docs): (DO NOT MERGE) strands integration#29
namrataghadi-galileo wants to merge 29 commits into
mainfrom
feature/strands-integration

Conversation

@namrataghadi-galileo

@namrataghadi-galileonamrataghadi-galileo commented Feb 13, 2026

Copy link
Copy Markdown
Contributor

Strands Integration Example
Demonstrates automatic safety controls for AWS Strands agents using a reusable hook pattern - no code decorators needed.

What's Included

Core Integration

  • AgentControlHook - Reusable hook class that intercepts Strands agent events and enforces controls
  • Automatic tool name extraction from Strands events for tool-specific controls
  • Support for both pre and post-stage evaluation across LLM calls and tool executions

Interactive Demo (interactive_demo/)

  • Customer support agent with Streamlit UI
  • Real-time PII detection (SSN, email, credit card blocking)
  • SQL injection prevention for database queries
  • Shows control violations with user-friendly error messages

Steering Demo (steering_demo/)

  • Banking email agent with PII redaction
  • Combines AgentControl detection + Strands Steering correction
  • Demonstrates layered governance: detect sensitive data, then intelligently redact it
  • Shows integration between two safety systems

Hook-Based Architecture

  • Attaches to Strands agents via the hooks parameter
  • Intercepts BeforeToolCallEvent, AfterModelCallEvent, and other Strands lifecycle events
  • Automatically constructs Step payloads and evaluates controls server-side
  • No modification to agent code or tool definitions required

@namrataghadi-galileo
namrataghadi-galileo changed the base branch from main to feature/54252-step_name-as_parameterFebruary 13, 2026 22:41
@codecov

codecovBot commented Feb 13, 2026

Copy link
Copy Markdown

The author of this PR, namrataghadi-galileo, is not an activated member of this organization on Codecov.
Please activate this user on Codecov to display this PR comment.
Coverage data is still being uploaded to Codecov.io for purposes of overall coverage calculations.
Please don't hesitate to email us at support@codecov.io with any questions.

Comment threadexamples/strands_integration/interactive_demo/interactive_support_demo.py Outdated
Base automatically changed from feature/54252-step_name-as_parameter to mainFebruary 17, 2026 17:00
@namrataghadi-galileo
namrataghadi-galileo changed the base branch from main to feature/add-stage-overrideFebruary 19, 2026 00:20
@namrataghadi-galileo
namrataghadi-galileo changed the base branch from feature/add-stage-override to mainFebruary 21, 2026 02:52
Comment threadsdks/python/src/agent_control/evaluation.py Outdated
Comment threadsdks/python/src/agent_control/evaluation.py Outdated
Comment threadsdks/python/src/agent_control/evaluation.py Outdated
Comment threadsdks/python/src/agent_control/evaluation.py Outdated

@lan17lan17 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM overall, just a few remaining nits, but i love the more simplified version of evaluate :)

async def evaluate_controls(
step_name: str,
*,
input: Any = None,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be Any | None? not sure if Any covers None

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, in what situations would this be null?

Same question for the output.

I suppose it would only be the case if there's no args to a tool call and a void output?

context: dict[str, Any] | None = None,
step_type: Literal["tool", "llm"] = "llm",
stage: Literal["pre", "post"] = "pre",
agent_uuid: str | UUID,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wonder if we should get rid of it since im getting rid of it here: #44

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will have to uptake it once your PR is merged.

output_text = ""

# BeforeInvocationEvent - has messages attribute with user input
if hasattr(event, "messages") and event.messages:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explicit event type checks would be preferable IMHO - for type safety purposes and readability

Same for below for tools

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure.

# selected_tool is an AgentTool object with name attribute
if hasattr(event.selected_tool, "name"):
tool_name = event.selected_tool.name
elif hasattr(event.selected_tool, "__name__"):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you see a case where you got something that wasn't AgentTool? Same question for below and "get"

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type check refactor takes care of this redundancy

if isinstance(event.tool_use, dict):
tool_name = event.tool_use.get("toolName", "unknown-tool")
elif hasattr(event.tool_use, "get"):
tool_name = event.tool_use.get("toolName", "unknown-tool")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar question to the need for "get" here

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed. The original code had overly defensive hasattr() checks that don't correspond to any real case in Strands. Refactored code correctly removes these unnecessary checks and uses the actual types

print(f"🔍 Extracted tool name: {tool_name}")

# Extract input (pre-stage) or output (post-stage)
if event_phase == "pre":

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you did an isinstance check for BeforeToolCall and AfterToolCall, I think you could even omit the event_phase here?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switching to an isinstance check let us remove a lot of code — great catch. This is handled there now.

content = content.content

# List of content blocks
if isinstance(content, list):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you ever want anything other than plain text?

Our AgentResult also stringifies structured output and citation blocks

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hooks intercept at a lower level (model/tool calls) BEFORE AgentResult is constructed. What we receive from the events is:

Message.content: list[ContentBlock]
ToolResult.content: list[ToolResultContent]
invocation_state values: Any (strings, dicts, etc.)

So we need more than plain text—thanks for flagging it. We now extract text, citations, tool info, and JSON from ContentBlock/ToolResultContent TypedDicts for control inspection, since those fields can contain content that needs policy enforcement.

@namrataghadi-galileonamrataghadi-galileo changed the title fix(docs): strands integrationfix(docs): (DO NOT MERGE) strands integrationMar 5, 2026
@namrataghadi-galileo
namrataghadi-galileo deleted the feature/strands-integration branch March 10, 2026 05:00
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@namrataghadi-galileo@yashsheth46@zastrowm@lan17
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(docs): (DO NOT MERGE) strands integration - #29

Closed
namrataghadi-galileo wants to merge 29 commits into
mainfrom
feature/strands-integration
Closed

fix(docs): (DO NOT MERGE) strands integration#29
namrataghadi-galileo wants to merge 29 commits into
mainfrom
feature/strands-integration

Conversation

@namrataghadi-galileo

@namrataghadi-galileonamrataghadi-galileo commented Feb 13, 2026

Copy link
Copy Markdown
Contributor

Strands Integration Example
Demonstrates automatic safety controls for AWS Strands agents using a reusable hook pattern - no code decorators needed.

What's Included

Core Integration

  • AgentControlHook - Reusable hook class that intercepts Strands agent events and enforces controls
  • Automatic tool name extraction from Strands events for tool-specific controls
  • Support for both pre and post-stage evaluation across LLM calls and tool executions

Interactive Demo (interactive_demo/)

  • Customer support agent with Streamlit UI
  • Real-time PII detection (SSN, email, credit card blocking)
  • SQL injection prevention for database queries
  • Shows control violations with user-friendly error messages

Steering Demo (steering_demo/)

  • Banking email agent with PII redaction
  • Combines AgentControl detection + Strands Steering correction
  • Demonstrates layered governance: detect sensitive data, then intelligently redact it
  • Shows integration between two safety systems

Hook-Based Architecture

  • Attaches to Strands agents via the hooks parameter
  • Intercepts BeforeToolCallEvent, AfterModelCallEvent, and other Strands lifecycle events
  • Automatically constructs Step payloads and evaluates controls server-side
  • No modification to agent code or tool definitions required

@namrataghadi-galileo
namrataghadi-galileo changed the base branch from main to feature/54252-step_name-as_parameterFebruary 13, 2026 22:41
@codecov

codecovBot commented Feb 13, 2026

Copy link
Copy Markdown

The author of this PR, namrataghadi-galileo, is not an activated member of this organization on Codecov.
Please activate this user on Codecov to display this PR comment.
Coverage data is still being uploaded to Codecov.io for purposes of overall coverage calculations.
Please don't hesitate to email us at support@codecov.io with any questions.

Comment threadexamples/strands_integration/interactive_demo/interactive_support_demo.py Outdated
Base automatically changed from feature/54252-step_name-as_parameter to mainFebruary 17, 2026 17:00
@namrataghadi-galileo
namrataghadi-galileo changed the base branch from main to feature/add-stage-overrideFebruary 19, 2026 00:20
@namrataghadi-galileo
namrataghadi-galileo changed the base branch from feature/add-stage-override to mainFebruary 21, 2026 02:52
Comment threadsdks/python/src/agent_control/evaluation.py Outdated
Comment threadsdks/python/src/agent_control/evaluation.py Outdated
Comment threadsdks/python/src/agent_control/evaluation.py Outdated
Comment threadsdks/python/src/agent_control/evaluation.py Outdated

@lan17lan17 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM overall, just a few remaining nits, but i love the more simplified version of evaluate :)

async def evaluate_controls(
step_name: str,
*,
input: Any = None,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be Any | None? not sure if Any covers None

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, in what situations would this be null?

Same question for the output.

I suppose it would only be the case if there's no args to a tool call and a void output?

context: dict[str, Any] | None = None,
step_type: Literal["tool", "llm"] = "llm",
stage: Literal["pre", "post"] = "pre",
agent_uuid: str | UUID,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wonder if we should get rid of it since im getting rid of it here: #44

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will have to uptake it once your PR is merged.

output_text = ""

# BeforeInvocationEvent - has messages attribute with user input
if hasattr(event, "messages") and event.messages:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explicit event type checks would be preferable IMHO - for type safety purposes and readability

Same for below for tools

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure.

# selected_tool is an AgentTool object with name attribute
if hasattr(event.selected_tool, "name"):
tool_name = event.selected_tool.name
elif hasattr(event.selected_tool, "__name__"):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you see a case where you got something that wasn't AgentTool? Same question for below and "get"

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type check refactor takes care of this redundancy

if isinstance(event.tool_use, dict):
tool_name = event.tool_use.get("toolName", "unknown-tool")
elif hasattr(event.tool_use, "get"):
tool_name = event.tool_use.get("toolName", "unknown-tool")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar question to the need for "get" here

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed. The original code had overly defensive hasattr() checks that don't correspond to any real case in Strands. Refactored code correctly removes these unnecessary checks and uses the actual types

print(f"🔍 Extracted tool name: {tool_name}")

# Extract input (pre-stage) or output (post-stage)
if event_phase == "pre":

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you did an isinstance check for BeforeToolCall and AfterToolCall, I think you could even omit the event_phase here?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switching to an isinstance check let us remove a lot of code — great catch. This is handled there now.

content = content.content

# List of content blocks
if isinstance(content, list):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you ever want anything other than plain text?

Our AgentResult also stringifies structured output and citation blocks

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hooks intercept at a lower level (model/tool calls) BEFORE AgentResult is constructed. What we receive from the events is:

Message.content: list[ContentBlock]
ToolResult.content: list[ToolResultContent]
invocation_state values: Any (strings, dicts, etc.)

So we need more than plain text—thanks for flagging it. We now extract text, citations, tool info, and JSON from ContentBlock/ToolResultContent TypedDicts for control inspection, since those fields can contain content that needs policy enforcement.

@namrataghadi-galileonamrataghadi-galileo changed the title fix(docs): strands integrationfix(docs): (DO NOT MERGE) strands integrationMar 5, 2026
@namrataghadi-galileo
namrataghadi-galileo deleted the feature/strands-integration branch March 10, 2026 05:00
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@namrataghadi-galileo@yashsheth46@zastrowm@lan17
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(docs): (DO NOT MERGE) strands integration - #29

Closed
namrataghadi-galileo wants to merge 29 commits into
mainfrom
feature/strands-integration
Closed

fix(docs): (DO NOT MERGE) strands integration#29
namrataghadi-galileo wants to merge 29 commits into
mainfrom
feature/strands-integration

Conversation

@namrataghadi-galileo

@namrataghadi-galileonamrataghadi-galileo commented Feb 13, 2026

Copy link
Copy Markdown
Contributor

Strands Integration Example
Demonstrates automatic safety controls for AWS Strands agents using a reusable hook pattern - no code decorators needed.

What's Included

Core Integration

  • AgentControlHook - Reusable hook class that intercepts Strands agent events and enforces controls
  • Automatic tool name extraction from Strands events for tool-specific controls
  • Support for both pre and post-stage evaluation across LLM calls and tool executions

Interactive Demo (interactive_demo/)

  • Customer support agent with Streamlit UI
  • Real-time PII detection (SSN, email, credit card blocking)
  • SQL injection prevention for database queries
  • Shows control violations with user-friendly error messages

Steering Demo (steering_demo/)

  • Banking email agent with PII redaction
  • Combines AgentControl detection + Strands Steering correction
  • Demonstrates layered governance: detect sensitive data, then intelligently redact it
  • Shows integration between two safety systems

Hook-Based Architecture

  • Attaches to Strands agents via the hooks parameter
  • Intercepts BeforeToolCallEvent, AfterModelCallEvent, and other Strands lifecycle events
  • Automatically constructs Step payloads and evaluates controls server-side
  • No modification to agent code or tool definitions required

@namrataghadi-galileo
namrataghadi-galileo changed the base branch from main to feature/54252-step_name-as_parameterFebruary 13, 2026 22:41
@codecov

codecovBot commented Feb 13, 2026

Copy link
Copy Markdown

The author of this PR, namrataghadi-galileo, is not an activated member of this organization on Codecov.
Please activate this user on Codecov to display this PR comment.
Coverage data is still being uploaded to Codecov.io for purposes of overall coverage calculations.
Please don't hesitate to email us at support@codecov.io with any questions.

Comment threadexamples/strands_integration/interactive_demo/interactive_support_demo.py Outdated
Base automatically changed from feature/54252-step_name-as_parameter to mainFebruary 17, 2026 17:00
@namrataghadi-galileo
namrataghadi-galileo changed the base branch from main to feature/add-stage-overrideFebruary 19, 2026 00:20
@namrataghadi-galileo
namrataghadi-galileo changed the base branch from feature/add-stage-override to mainFebruary 21, 2026 02:52
Comment threadsdks/python/src/agent_control/evaluation.py Outdated
Comment threadsdks/python/src/agent_control/evaluation.py Outdated
Comment threadsdks/python/src/agent_control/evaluation.py Outdated
Comment threadsdks/python/src/agent_control/evaluation.py Outdated

@lan17lan17 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM overall, just a few remaining nits, but i love the more simplified version of evaluate :)

async def evaluate_controls(
step_name: str,
*,
input: Any = None,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be Any | None? not sure if Any covers None

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, in what situations would this be null?

Same question for the output.

I suppose it would only be the case if there's no args to a tool call and a void output?

context: dict[str, Any] | None = None,
step_type: Literal["tool", "llm"] = "llm",
stage: Literal["pre", "post"] = "pre",
agent_uuid: str | UUID,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wonder if we should get rid of it since im getting rid of it here: #44

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will have to uptake it once your PR is merged.

output_text = ""

# BeforeInvocationEvent - has messages attribute with user input
if hasattr(event, "messages") and event.messages:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explicit event type checks would be preferable IMHO - for type safety purposes and readability

Same for below for tools

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure.

# selected_tool is an AgentTool object with name attribute
if hasattr(event.selected_tool, "name"):
tool_name = event.selected_tool.name
elif hasattr(event.selected_tool, "__name__"):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you see a case where you got something that wasn't AgentTool? Same question for below and "get"

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type check refactor takes care of this redundancy

if isinstance(event.tool_use, dict):
tool_name = event.tool_use.get("toolName", "unknown-tool")
elif hasattr(event.tool_use, "get"):
tool_name = event.tool_use.get("toolName", "unknown-tool")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar question to the need for "get" here

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed. The original code had overly defensive hasattr() checks that don't correspond to any real case in Strands. Refactored code correctly removes these unnecessary checks and uses the actual types

print(f"🔍 Extracted tool name: {tool_name}")

# Extract input (pre-stage) or output (post-stage)
if event_phase == "pre":

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you did an isinstance check for BeforeToolCall and AfterToolCall, I think you could even omit the event_phase here?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switching to an isinstance check let us remove a lot of code — great catch. This is handled there now.

content = content.content

# List of content blocks
if isinstance(content, list):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you ever want anything other than plain text?

Our AgentResult also stringifies structured output and citation blocks

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hooks intercept at a lower level (model/tool calls) BEFORE AgentResult is constructed. What we receive from the events is:

Message.content: list[ContentBlock]
ToolResult.content: list[ToolResultContent]
invocation_state values: Any (strings, dicts, etc.)

So we need more than plain text—thanks for flagging it. We now extract text, citations, tool info, and JSON from ContentBlock/ToolResultContent TypedDicts for control inspection, since those fields can contain content that needs policy enforcement.

@namrataghadi-galileonamrataghadi-galileo changed the title fix(docs): strands integrationfix(docs): (DO NOT MERGE) strands integrationMar 5, 2026
@namrataghadi-galileo
namrataghadi-galileo deleted the feature/strands-integration branch March 10, 2026 05:00
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@namrataghadi-galileo@yashsheth46@zastrowm@lan17
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(docs): (DO NOT MERGE) strands integration - #29

Closed
namrataghadi-galileo wants to merge 29 commits into
mainfrom
feature/strands-integration
Closed

fix(docs): (DO NOT MERGE) strands integration#29
namrataghadi-galileo wants to merge 29 commits into
mainfrom
feature/strands-integration

Conversation

@namrataghadi-galileo

@namrataghadi-galileonamrataghadi-galileo commented Feb 13, 2026

Copy link
Copy Markdown
Contributor

Strands Integration Example
Demonstrates automatic safety controls for AWS Strands agents using a reusable hook pattern - no code decorators needed.

What's Included

Core Integration

  • AgentControlHook - Reusable hook class that intercepts Strands agent events and enforces controls
  • Automatic tool name extraction from Strands events for tool-specific controls
  • Support for both pre and post-stage evaluation across LLM calls and tool executions

Interactive Demo (interactive_demo/)

  • Customer support agent with Streamlit UI
  • Real-time PII detection (SSN, email, credit card blocking)
  • SQL injection prevention for database queries
  • Shows control violations with user-friendly error messages

Steering Demo (steering_demo/)

  • Banking email agent with PII redaction
  • Combines AgentControl detection + Strands Steering correction
  • Demonstrates layered governance: detect sensitive data, then intelligently redact it
  • Shows integration between two safety systems

Hook-Based Architecture

  • Attaches to Strands agents via the hooks parameter
  • Intercepts BeforeToolCallEvent, AfterModelCallEvent, and other Strands lifecycle events
  • Automatically constructs Step payloads and evaluates controls server-side
  • No modification to agent code or tool definitions required

@namrataghadi-galileo
namrataghadi-galileo changed the base branch from main to feature/54252-step_name-as_parameterFebruary 13, 2026 22:41
@codecov

codecovBot commented Feb 13, 2026

Copy link
Copy Markdown

The author of this PR, namrataghadi-galileo, is not an activated member of this organization on Codecov.
Please activate this user on Codecov to display this PR comment.
Coverage data is still being uploaded to Codecov.io for purposes of overall coverage calculations.
Please don't hesitate to email us at support@codecov.io with any questions.

Comment threadexamples/strands_integration/interactive_demo/interactive_support_demo.py Outdated
Base automatically changed from feature/54252-step_name-as_parameter to mainFebruary 17, 2026 17:00
@namrataghadi-galileo
namrataghadi-galileo changed the base branch from main to feature/add-stage-overrideFebruary 19, 2026 00:20
@namrataghadi-galileo
namrataghadi-galileo changed the base branch from feature/add-stage-override to mainFebruary 21, 2026 02:52
Comment threadsdks/python/src/agent_control/evaluation.py Outdated
Comment threadsdks/python/src/agent_control/evaluation.py Outdated
Comment threadsdks/python/src/agent_control/evaluation.py Outdated
Comment threadsdks/python/src/agent_control/evaluation.py Outdated

@lan17lan17 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM overall, just a few remaining nits, but i love the more simplified version of evaluate :)

async def evaluate_controls(
step_name: str,
*,
input: Any = None,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be Any | None? not sure if Any covers None

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, in what situations would this be null?

Same question for the output.

I suppose it would only be the case if there's no args to a tool call and a void output?

context: dict[str, Any] | None = None,
step_type: Literal["tool", "llm"] = "llm",
stage: Literal["pre", "post"] = "pre",
agent_uuid: str | UUID,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wonder if we should get rid of it since im getting rid of it here: #44

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will have to uptake it once your PR is merged.

output_text = ""

# BeforeInvocationEvent - has messages attribute with user input
if hasattr(event, "messages") and event.messages:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explicit event type checks would be preferable IMHO - for type safety purposes and readability

Same for below for tools

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure.

# selected_tool is an AgentTool object with name attribute
if hasattr(event.selected_tool, "name"):
tool_name = event.selected_tool.name
elif hasattr(event.selected_tool, "__name__"):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you see a case where you got something that wasn't AgentTool? Same question for below and "get"

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type check refactor takes care of this redundancy

if isinstance(event.tool_use, dict):
tool_name = event.tool_use.get("toolName", "unknown-tool")
elif hasattr(event.tool_use, "get"):
tool_name = event.tool_use.get("toolName", "unknown-tool")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar question to the need for "get" here

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed. The original code had overly defensive hasattr() checks that don't correspond to any real case in Strands. Refactored code correctly removes these unnecessary checks and uses the actual types

print(f"🔍 Extracted tool name: {tool_name}")

# Extract input (pre-stage) or output (post-stage)
if event_phase == "pre":

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you did an isinstance check for BeforeToolCall and AfterToolCall, I think you could even omit the event_phase here?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switching to an isinstance check let us remove a lot of code — great catch. This is handled there now.

content = content.content

# List of content blocks
if isinstance(content, list):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you ever want anything other than plain text?

Our AgentResult also stringifies structured output and citation blocks

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hooks intercept at a lower level (model/tool calls) BEFORE AgentResult is constructed. What we receive from the events is:

Message.content: list[ContentBlock]
ToolResult.content: list[ToolResultContent]
invocation_state values: Any (strings, dicts, etc.)

So we need more than plain text—thanks for flagging it. We now extract text, citations, tool info, and JSON from ContentBlock/ToolResultContent TypedDicts for control inspection, since those fields can contain content that needs policy enforcement.

@namrataghadi-galileonamrataghadi-galileo changed the title fix(docs): strands integrationfix(docs): (DO NOT MERGE) strands integrationMar 5, 2026
@namrataghadi-galileo
namrataghadi-galileo deleted the feature/strands-integration branch March 10, 2026 05:00
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@namrataghadi-galileo@yashsheth46@zastrowm@lan17
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(docs): (DO NOT MERGE) strands integration - #29

Closed
namrataghadi-galileo wants to merge 29 commits into
mainfrom
feature/strands-integration
Closed

fix(docs): (DO NOT MERGE) strands integration#29
namrataghadi-galileo wants to merge 29 commits into
mainfrom
feature/strands-integration

Conversation

@namrataghadi-galileo

@namrataghadi-galileonamrataghadi-galileo commented Feb 13, 2026

Copy link
Copy Markdown
Contributor

Strands Integration Example
Demonstrates automatic safety controls for AWS Strands agents using a reusable hook pattern - no code decorators needed.

What's Included

Core Integration

  • AgentControlHook - Reusable hook class that intercepts Strands agent events and enforces controls
  • Automatic tool name extraction from Strands events for tool-specific controls
  • Support for both pre and post-stage evaluation across LLM calls and tool executions

Interactive Demo (interactive_demo/)

  • Customer support agent with Streamlit UI
  • Real-time PII detection (SSN, email, credit card blocking)
  • SQL injection prevention for database queries
  • Shows control violations with user-friendly error messages

Steering Demo (steering_demo/)

  • Banking email agent with PII redaction
  • Combines AgentControl detection + Strands Steering correction
  • Demonstrates layered governance: detect sensitive data, then intelligently redact it
  • Shows integration between two safety systems

Hook-Based Architecture

  • Attaches to Strands agents via the hooks parameter
  • Intercepts BeforeToolCallEvent, AfterModelCallEvent, and other Strands lifecycle events
  • Automatically constructs Step payloads and evaluates controls server-side
  • No modification to agent code or tool definitions required

@namrataghadi-galileo
namrataghadi-galileo changed the base branch from main to feature/54252-step_name-as_parameterFebruary 13, 2026 22:41
@codecov

codecovBot commented Feb 13, 2026

Copy link
Copy Markdown

The author of this PR, namrataghadi-galileo, is not an activated member of this organization on Codecov.
Please activate this user on Codecov to display this PR comment.
Coverage data is still being uploaded to Codecov.io for purposes of overall coverage calculations.
Please don't hesitate to email us at support@codecov.io with any questions.

Comment threadexamples/strands_integration/interactive_demo/interactive_support_demo.py Outdated
Base automatically changed from feature/54252-step_name-as_parameter to mainFebruary 17, 2026 17:00
@namrataghadi-galileo
namrataghadi-galileo changed the base branch from main to feature/add-stage-overrideFebruary 19, 2026 00:20
@namrataghadi-galileo
namrataghadi-galileo changed the base branch from feature/add-stage-override to mainFebruary 21, 2026 02:52
Comment threadsdks/python/src/agent_control/evaluation.py Outdated
Comment threadsdks/python/src/agent_control/evaluation.py Outdated
Comment threadsdks/python/src/agent_control/evaluation.py Outdated
Comment threadsdks/python/src/agent_control/evaluation.py Outdated

@lan17lan17 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM overall, just a few remaining nits, but i love the more simplified version of evaluate :)

async def evaluate_controls(
step_name: str,
*,
input: Any = None,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be Any | None? not sure if Any covers None

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, in what situations would this be null?

Same question for the output.

I suppose it would only be the case if there's no args to a tool call and a void output?

context: dict[str, Any] | None = None,
step_type: Literal["tool", "llm"] = "llm",
stage: Literal["pre", "post"] = "pre",
agent_uuid: str | UUID,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wonder if we should get rid of it since im getting rid of it here: #44

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will have to uptake it once your PR is merged.

output_text = ""

# BeforeInvocationEvent - has messages attribute with user input
if hasattr(event, "messages") and event.messages:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explicit event type checks would be preferable IMHO - for type safety purposes and readability

Same for below for tools

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure.

# selected_tool is an AgentTool object with name attribute
if hasattr(event.selected_tool, "name"):
tool_name = event.selected_tool.name
elif hasattr(event.selected_tool, "__name__"):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you see a case where you got something that wasn't AgentTool? Same question for below and "get"

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type check refactor takes care of this redundancy

if isinstance(event.tool_use, dict):
tool_name = event.tool_use.get("toolName", "unknown-tool")
elif hasattr(event.tool_use, "get"):
tool_name = event.tool_use.get("toolName", "unknown-tool")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar question to the need for "get" here

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed. The original code had overly defensive hasattr() checks that don't correspond to any real case in Strands. Refactored code correctly removes these unnecessary checks and uses the actual types

print(f"🔍 Extracted tool name: {tool_name}")

# Extract input (pre-stage) or output (post-stage)
if event_phase == "pre":

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you did an isinstance check for BeforeToolCall and AfterToolCall, I think you could even omit the event_phase here?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switching to an isinstance check let us remove a lot of code — great catch. This is handled there now.

content = content.content

# List of content blocks
if isinstance(content, list):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you ever want anything other than plain text?

Our AgentResult also stringifies structured output and citation blocks

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hooks intercept at a lower level (model/tool calls) BEFORE AgentResult is constructed. What we receive from the events is:

Message.content: list[ContentBlock]
ToolResult.content: list[ToolResultContent]
invocation_state values: Any (strings, dicts, etc.)

So we need more than plain text—thanks for flagging it. We now extract text, citations, tool info, and JSON from ContentBlock/ToolResultContent TypedDicts for control inspection, since those fields can contain content that needs policy enforcement.

@namrataghadi-galileonamrataghadi-galileo changed the title fix(docs): strands integrationfix(docs): (DO NOT MERGE) strands integrationMar 5, 2026
@namrataghadi-galileo
namrataghadi-galileo deleted the feature/strands-integration branch March 10, 2026 05:00
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@namrataghadi-galileo@yashsheth46@zastrowm@lan17
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(docs): (DO NOT MERGE) strands integration - #29

Closed
namrataghadi-galileo wants to merge 29 commits into
mainfrom
feature/strands-integration
Closed

fix(docs): (DO NOT MERGE) strands integration#29
namrataghadi-galileo wants to merge 29 commits into
mainfrom
feature/strands-integration

Conversation

@namrataghadi-galileo

@namrataghadi-galileonamrataghadi-galileo commented Feb 13, 2026

Copy link
Copy Markdown
Contributor

Strands Integration Example
Demonstrates automatic safety controls for AWS Strands agents using a reusable hook pattern - no code decorators needed.

What's Included

Core Integration

  • AgentControlHook - Reusable hook class that intercepts Strands agent events and enforces controls
  • Automatic tool name extraction from Strands events for tool-specific controls
  • Support for both pre and post-stage evaluation across LLM calls and tool executions

Interactive Demo (interactive_demo/)

  • Customer support agent with Streamlit UI
  • Real-time PII detection (SSN, email, credit card blocking)
  • SQL injection prevention for database queries
  • Shows control violations with user-friendly error messages

Steering Demo (steering_demo/)

  • Banking email agent with PII redaction
  • Combines AgentControl detection + Strands Steering correction
  • Demonstrates layered governance: detect sensitive data, then intelligently redact it
  • Shows integration between two safety systems

Hook-Based Architecture

  • Attaches to Strands agents via the hooks parameter
  • Intercepts BeforeToolCallEvent, AfterModelCallEvent, and other Strands lifecycle events
  • Automatically constructs Step payloads and evaluates controls server-side
  • No modification to agent code or tool definitions required

@namrataghadi-galileo
namrataghadi-galileo changed the base branch from main to feature/54252-step_name-as_parameterFebruary 13, 2026 22:41
@codecov

codecovBot commented Feb 13, 2026

Copy link
Copy Markdown

The author of this PR, namrataghadi-galileo, is not an activated member of this organization on Codecov.
Please activate this user on Codecov to display this PR comment.
Coverage data is still being uploaded to Codecov.io for purposes of overall coverage calculations.
Please don't hesitate to email us at support@codecov.io with any questions.

Comment threadexamples/strands_integration/interactive_demo/interactive_support_demo.py Outdated
Base automatically changed from feature/54252-step_name-as_parameter to mainFebruary 17, 2026 17:00
@namrataghadi-galileo
namrataghadi-galileo changed the base branch from main to feature/add-stage-overrideFebruary 19, 2026 00:20
@namrataghadi-galileo
namrataghadi-galileo changed the base branch from feature/add-stage-override to mainFebruary 21, 2026 02:52
Comment threadsdks/python/src/agent_control/evaluation.py Outdated
Comment threadsdks/python/src/agent_control/evaluation.py Outdated
Comment threadsdks/python/src/agent_control/evaluation.py Outdated
Comment threadsdks/python/src/agent_control/evaluation.py Outdated

@lan17lan17 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM overall, just a few remaining nits, but i love the more simplified version of evaluate :)

async def evaluate_controls(
step_name: str,
*,
input: Any = None,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be Any | None? not sure if Any covers None

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, in what situations would this be null?

Same question for the output.

I suppose it would only be the case if there's no args to a tool call and a void output?

context: dict[str, Any] | None = None,
step_type: Literal["tool", "llm"] = "llm",
stage: Literal["pre", "post"] = "pre",
agent_uuid: str | UUID,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wonder if we should get rid of it since im getting rid of it here: #44

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will have to uptake it once your PR is merged.

output_text = ""

# BeforeInvocationEvent - has messages attribute with user input
if hasattr(event, "messages") and event.messages:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explicit event type checks would be preferable IMHO - for type safety purposes and readability

Same for below for tools

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure.

# selected_tool is an AgentTool object with name attribute
if hasattr(event.selected_tool, "name"):
tool_name = event.selected_tool.name
elif hasattr(event.selected_tool, "__name__"):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you see a case where you got something that wasn't AgentTool? Same question for below and "get"

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type check refactor takes care of this redundancy

if isinstance(event.tool_use, dict):
tool_name = event.tool_use.get("toolName", "unknown-tool")
elif hasattr(event.tool_use, "get"):
tool_name = event.tool_use.get("toolName", "unknown-tool")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar question to the need for "get" here

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed. The original code had overly defensive hasattr() checks that don't correspond to any real case in Strands. Refactored code correctly removes these unnecessary checks and uses the actual types

print(f"🔍 Extracted tool name: {tool_name}")

# Extract input (pre-stage) or output (post-stage)
if event_phase == "pre":

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you did an isinstance check for BeforeToolCall and AfterToolCall, I think you could even omit the event_phase here?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switching to an isinstance check let us remove a lot of code — great catch. This is handled there now.

content = content.content

# List of content blocks
if isinstance(content, list):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you ever want anything other than plain text?

Our AgentResult also stringifies structured output and citation blocks

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hooks intercept at a lower level (model/tool calls) BEFORE AgentResult is constructed. What we receive from the events is:

Message.content: list[ContentBlock]
ToolResult.content: list[ToolResultContent]
invocation_state values: Any (strings, dicts, etc.)

So we need more than plain text—thanks for flagging it. We now extract text, citations, tool info, and JSON from ContentBlock/ToolResultContent TypedDicts for control inspection, since those fields can contain content that needs policy enforcement.

@namrataghadi-galileonamrataghadi-galileo changed the title fix(docs): strands integrationfix(docs): (DO NOT MERGE) strands integrationMar 5, 2026
@namrataghadi-galileo
namrataghadi-galileo deleted the feature/strands-integration branch March 10, 2026 05:00
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@namrataghadi-galileo@yashsheth46@zastrowm@lan17
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix(docs): (DO NOT MERGE) strands integration - #29

Closed
namrataghadi-galileo wants to merge 29 commits into
mainfrom
feature/strands-integration
Closed

fix(docs): (DO NOT MERGE) strands integration#29
namrataghadi-galileo wants to merge 29 commits into
mainfrom
feature/strands-integration

Conversation

@namrataghadi-galileo

@namrataghadi-galileonamrataghadi-galileo commented Feb 13, 2026

Copy link
Copy Markdown
Contributor

Strands Integration Example
Demonstrates automatic safety controls for AWS Strands agents using a reusable hook pattern - no code decorators needed.

What's Included

Core Integration

  • AgentControlHook - Reusable hook class that intercepts Strands agent events and enforces controls
  • Automatic tool name extraction from Strands events for tool-specific controls
  • Support for both pre and post-stage evaluation across LLM calls and tool executions

Interactive Demo (interactive_demo/)

  • Customer support agent with Streamlit UI
  • Real-time PII detection (SSN, email, credit card blocking)
  • SQL injection prevention for database queries
  • Shows control violations with user-friendly error messages

Steering Demo (steering_demo/)

  • Banking email agent with PII redaction
  • Combines AgentControl detection + Strands Steering correction
  • Demonstrates layered governance: detect sensitive data, then intelligently redact it
  • Shows integration between two safety systems

Hook-Based Architecture

  • Attaches to Strands agents via the hooks parameter
  • Intercepts BeforeToolCallEvent, AfterModelCallEvent, and other Strands lifecycle events
  • Automatically constructs Step payloads and evaluates controls server-side
  • No modification to agent code or tool definitions required

@namrataghadi-galileo
namrataghadi-galileo changed the base branch from main to feature/54252-step_name-as_parameterFebruary 13, 2026 22:41
@codecov

codecovBot commented Feb 13, 2026

Copy link
Copy Markdown

The author of this PR, namrataghadi-galileo, is not an activated member of this organization on Codecov.
Please activate this user on Codecov to display this PR comment.
Coverage data is still being uploaded to Codecov.io for purposes of overall coverage calculations.
Please don't hesitate to email us at support@codecov.io with any questions.

Comment threadexamples/strands_integration/interactive_demo/interactive_support_demo.py Outdated
Base automatically changed from feature/54252-step_name-as_parameter to mainFebruary 17, 2026 17:00
@namrataghadi-galileo
namrataghadi-galileo changed the base branch from main to feature/add-stage-overrideFebruary 19, 2026 00:20
@namrataghadi-galileo
namrataghadi-galileo changed the base branch from feature/add-stage-override to mainFebruary 21, 2026 02:52
Comment threadsdks/python/src/agent_control/evaluation.py Outdated
Comment threadsdks/python/src/agent_control/evaluation.py Outdated
Comment threadsdks/python/src/agent_control/evaluation.py Outdated
Comment threadsdks/python/src/agent_control/evaluation.py Outdated

@lan17lan17 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM overall, just a few remaining nits, but i love the more simplified version of evaluate :)

async def evaluate_controls(
step_name: str,
*,
input: Any = None,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be Any | None? not sure if Any covers None

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, in what situations would this be null?

Same question for the output.

I suppose it would only be the case if there's no args to a tool call and a void output?

context: dict[str, Any] | None = None,
step_type: Literal["tool", "llm"] = "llm",
stage: Literal["pre", "post"] = "pre",
agent_uuid: str | UUID,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wonder if we should get rid of it since im getting rid of it here: #44

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will have to uptake it once your PR is merged.

output_text = ""

# BeforeInvocationEvent - has messages attribute with user input
if hasattr(event, "messages") and event.messages:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explicit event type checks would be preferable IMHO - for type safety purposes and readability

Same for below for tools

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure.

# selected_tool is an AgentTool object with name attribute
if hasattr(event.selected_tool, "name"):
tool_name = event.selected_tool.name
elif hasattr(event.selected_tool, "__name__"):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you see a case where you got something that wasn't AgentTool? Same question for below and "get"

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type check refactor takes care of this redundancy

if isinstance(event.tool_use, dict):
tool_name = event.tool_use.get("toolName", "unknown-tool")
elif hasattr(event.tool_use, "get"):
tool_name = event.tool_use.get("toolName", "unknown-tool")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar question to the need for "get" here

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed. The original code had overly defensive hasattr() checks that don't correspond to any real case in Strands. Refactored code correctly removes these unnecessary checks and uses the actual types

print(f"🔍 Extracted tool name: {tool_name}")

# Extract input (pre-stage) or output (post-stage)
if event_phase == "pre":

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you did an isinstance check for BeforeToolCall and AfterToolCall, I think you could even omit the event_phase here?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switching to an isinstance check let us remove a lot of code — great catch. This is handled there now.

content = content.content

# List of content blocks
if isinstance(content, list):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you ever want anything other than plain text?

Our AgentResult also stringifies structured output and citation blocks

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hooks intercept at a lower level (model/tool calls) BEFORE AgentResult is constructed. What we receive from the events is:

Message.content: list[ContentBlock]
ToolResult.content: list[ToolResultContent]
invocation_state values: Any (strings, dicts, etc.)

So we need more than plain text—thanks for flagging it. We now extract text, citations, tool info, and JSON from ContentBlock/ToolResultContent TypedDicts for control inspection, since those fields can contain content that needs policy enforcement.

@namrataghadi-galileonamrataghadi-galileo changed the title fix(docs): strands integrationfix(docs): (DO NOT MERGE) strands integrationMar 5, 2026
@namrataghadi-galileo
namrataghadi-galileo deleted the feature/strands-integration branch March 10, 2026 05:00
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@namrataghadi-galileo@yashsheth46@zastrowm@lan17